summaryrefslogtreecommitdiffstats
path: root/vespa-athenz
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-03-20 13:23:07 +0100
committerBjørn Christian Seime <bjorncs@oath.com>2018-03-20 14:05:29 +0100
commitd6ba1b913fbcfa69387ddb6390b1bfa057753e2a (patch)
tree4dfa6c6e6f08e15a706ac711b581c351431c5282 /vespa-athenz
parent1cfd08c3c3eb48dce0bf59ea71592438aeaefe96 (diff)
Add getters for basic constraints and subject alternative names
Diffstat (limited to 'vespa-athenz')
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/BasicConstraintsExtension.java14
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Extension.java22
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10Csr.java68
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrBuilder.java18
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateBuilder.java9
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java7
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrTest.java54
7 files changed, 175 insertions, 17 deletions
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/BasicConstraintsExtension.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/BasicConstraintsExtension.java
new file mode 100644
index 00000000000..008268dbfe0
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/BasicConstraintsExtension.java
@@ -0,0 +1,14 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.tls;
+
+/**
+ * @author bjorncs
+ */
+class BasicConstraintsExtension {
+ final boolean isCritical, isCertAuthorityCertificate;
+
+ BasicConstraintsExtension(boolean isCritical, boolean isCertAuthorityCertificate) {
+ this.isCritical = isCritical;
+ this.isCertAuthorityCertificate = isCertAuthorityCertificate;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Extension.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Extension.java
new file mode 100644
index 00000000000..18403669c4d
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Extension.java
@@ -0,0 +1,22 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.tls;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * @author bjorncs
+ */
+public enum Extension {
+ BASIC_CONSTRAINS(org.bouncycastle.asn1.x509.Extension.basicConstraints),
+ SUBJECT_ALTERNATIVE_NAMES(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName);
+
+ final ASN1ObjectIdentifier extensionOId;
+
+ Extension(ASN1ObjectIdentifier extensionOId) {
+ this.extensionOId = extensionOId;
+ }
+
+ public String getOId() {
+ return extensionOId.getId();
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10Csr.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10Csr.java
index 7a43b5a1fd5..061a70872f4 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10Csr.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10Csr.java
@@ -1,13 +1,25 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.tls;
-import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import javax.security.auth.x500.X500Principal;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.UncheckedIOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.toList;
/**
* @author bjorncs
@@ -27,4 +39,52 @@ public class Pkcs10Csr {
public X500Principal getSubject() {
return new X500Principal(csr.getSubject().toString());
}
+
+ public List<String> getSubjectAlternativeNames() {
+ return getExtensions()
+ .map(extensions -> GeneralNames.fromExtensions(extensions, Extension.subjectAlternativeName))
+ .filter(Objects::nonNull)
+ .flatMap(generalNames -> Arrays.stream(generalNames.getNames()))
+ .map(Pkcs10Csr::toString)
+ .collect(toList());
+ }
+
+ /**
+ * @return true if CA certificate
+ */
+ public boolean getBasicConstraints() {
+ return getExtensions()
+ .map(BasicConstraints::fromExtensions)
+ .findAny()
+ .map(BasicConstraints::isCA)
+ .orElse(false);
+ }
+
+ public List<String> getExtensionOIds() {
+ return getExtensions()
+ .flatMap(extensions -> Arrays.stream(extensions.getExtensionOIDs()))
+ .map(ASN1ObjectIdentifier::getId)
+ .collect(toList());
+
+ }
+
+ private Stream<Extensions> getExtensions() {
+ return Arrays
+ .stream(csr.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest))
+ .map(attribute -> Extensions.getInstance(attribute.getAttrValues().getObjectAt(0)));
+ }
+
+ private static String toString(GeneralName generalName) {
+ ASN1Encodable name = generalName.getName();
+ switch (generalName.getTagNo()) {
+ case GeneralName.rfc822Name:
+ case GeneralName.dNSName:
+ case GeneralName.uniformResourceIdentifier:
+ return DERIA5String.getInstance(name).getString();
+ case GeneralName.directoryName:
+ return X500Name.getInstance(name).toString();
+ default:
+ return name.toString();
+ }
+ }
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrBuilder.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrBuilder.java
index 877f05f998a..9c2cd9d4d9f 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrBuilder.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrBuilder.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.athenz.tls;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
@@ -28,6 +29,7 @@ public class Pkcs10CsrBuilder {
private final KeyPair keyPair;
private final List<String> subjectAlternativeNames = new ArrayList<>();
private final SignatureAlgorithm signatureAlgorithm;
+ private BasicConstraintsExtension basicConstraintsExtension;
private Pkcs10CsrBuilder(X500Principal subject,
KeyPair keyPair,
@@ -48,20 +50,30 @@ public class Pkcs10CsrBuilder {
return this;
}
+ public Pkcs10CsrBuilder setBasicConstraints(boolean isCritical, boolean isCertAuthorityCertificate) {
+ this.basicConstraintsExtension = new BasicConstraintsExtension(isCritical, isCertAuthorityCertificate);
+ return this;
+ }
+
public Pkcs10Csr build() {
try {
PKCS10CertificationRequestBuilder requestBuilder =
new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic());
-
+ ExtensionsGenerator extGen = new ExtensionsGenerator();
+ if (basicConstraintsExtension != null) {
+ extGen.addExtension(
+ Extension.basicConstraints,
+ basicConstraintsExtension.isCritical,
+ new BasicConstraints(basicConstraintsExtension.isCertAuthorityCertificate));
+ }
if (!subjectAlternativeNames.isEmpty()) {
GeneralNames generalNames = new GeneralNames(
subjectAlternativeNames.stream()
.map(san -> new GeneralName(GeneralName.dNSName, san))
.toArray(GeneralName[]::new));
- ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(Extension.subjectAlternativeName, false, generalNames);
- requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
}
+ requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm.getAlgorithmName())
.setProvider(BouncyCastleProviderHolder.getInstance())
.build(keyPair.getPrivate());
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateBuilder.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateBuilder.java
index e60592db2f5..ba22af13fd2 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateBuilder.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateBuilder.java
@@ -7,7 +7,6 @@ import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
@@ -143,12 +142,4 @@ public class X509CertificateBuilder {
}
}
- private static class BasicConstraintsExtension {
- final boolean isCritical, isCertAuthorityCertificate;
-
- BasicConstraintsExtension(boolean isCritical, boolean isCertAuthorityCertificate) {
- this.isCritical = isCritical;
- this.isCertAuthorityCertificate = isCertAuthorityCertificate;
- }
- }
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java
index 4c4399cd2cb..3e1bd3eb31c 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/X509CertificateUtils.java
@@ -9,6 +9,7 @@ import org.bouncycastle.util.io.pem.PemObject;
import javax.naming.NamingException;
import javax.naming.ldap.LdapName;
+import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
@@ -59,8 +60,12 @@ public class X509CertificateUtils {
}
public static List<String> getCommonNames(X509Certificate certificate) {
+ return getCommonNames(certificate.getSubjectX500Principal());
+ }
+
+ public static List<String> getCommonNames(X500Principal subject) {
try {
- String subjectPrincipal = certificate.getSubjectX500Principal().getName();
+ String subjectPrincipal = subject.getName();
return new LdapName(subjectPrincipal).getRdns().stream()
.filter(rdn -> rdn.getType().equalsIgnoreCase("cn"))
.map(rdn -> rdn.getValue().toString())
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrTest.java
new file mode 100644
index 00000000000..9586906668d
--- /dev/null
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/tls/Pkcs10CsrTest.java
@@ -0,0 +1,54 @@
+package com.yahoo.vespa.athenz.tls;
+
+import org.junit.Test;
+
+import javax.security.auth.x500.X500Principal;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author bjorncs
+ */
+public class Pkcs10CsrTest {
+
+ @Test
+ public void can_read_subject_alternative_names() {
+ X500Principal subject = new X500Principal("CN=subject");
+ KeyPair keypair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
+ String san1 = "san1.com";
+ String san2 = "san2.com";
+ Pkcs10Csr csr = Pkcs10CsrBuilder.fromKeypair(subject, keypair, SignatureAlgorithm.SHA256_WITH_RSA)
+ .addSubjectAlternativeName(san1)
+ .addSubjectAlternativeName(san2)
+ .build();
+ assertEquals(Arrays.asList(san1, san2), csr.getSubjectAlternativeNames());
+ }
+
+ @Test
+ public void can_read_basic_constraints() {
+ X500Principal subject = new X500Principal("CN=subject");
+ KeyPair keypair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
+ Pkcs10Csr csr = Pkcs10CsrBuilder.fromKeypair(subject, keypair, SignatureAlgorithm.SHA256_WITH_RSA)
+ .setBasicConstraints(true, true)
+ .build();
+ assertTrue(csr.getBasicConstraints());
+ }
+
+ @Test
+ public void can_read_extensions() {
+ X500Principal subject = new X500Principal("CN=subject");
+ KeyPair keypair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
+ Pkcs10Csr csr = Pkcs10CsrBuilder.fromKeypair(subject, keypair, SignatureAlgorithm.SHA256_WITH_RSA)
+ .addSubjectAlternativeName("san")
+ .setBasicConstraints(true, true)
+ .build();
+ List<String> expected = Arrays.asList(Extension.BASIC_CONSTRAINS.getOId(), Extension.SUBJECT_ALTERNATIVE_NAMES.getOId());
+ List<String> actual = csr.getExtensionOIds();
+ assertEquals(expected, actual);
+ }
+
+} \ No newline at end of file