diff options
author | Bjørn Christian Seime <bjorncs@oath.com> | 2018-03-20 14:06:51 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@oath.com> | 2018-03-22 13:01:07 +0100 |
commit | f32250ae4e1385bd369bcb29397c4679d384caa0 (patch) | |
tree | 6d7ce7f7b5d66635d3ca44ff7e7edd5922ee7259 /athenz-identity-provider-service/src/main | |
parent | 77d4cfae0c5aab772bad825ac88c71fa505e6a38 (diff) |
Use Pkcs10Csr and related types in CertificateSigner
Diffstat (limited to 'athenz-identity-provider-service/src/main')
4 files changed, 41 insertions, 95 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java index 25733bf0075..cfef2bc0e33 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java @@ -7,12 +7,9 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.bouncycastle.util.io.pem.PemObject; +import com.yahoo.vespa.athenz.tls.X509CertificateUtils; import java.io.IOException; -import java.io.StringWriter; -import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; /** @@ -56,13 +53,7 @@ public class CertificateSerializedPayload { @Override public void serialize( X509Certificate certificate, JsonGenerator gen, SerializerProvider serializers) throws IOException { - try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { - pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded())); - pemWriter.flush(); - gen.writeString(stringWriter.toString()); - } catch (CertificateEncodingException e) { - throw new RuntimeException("Failed to encode X509Certificate", e); - } + gen.writeString(X509CertificateUtils.toPem(certificate)); } } } diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java index f6f6bb1dbca..7b4a599d5dd 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java @@ -6,41 +6,23 @@ import com.google.inject.Inject; import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.config.provision.Zone; import com.yahoo.log.LogLevel; +import com.yahoo.vespa.athenz.tls.Extension; +import com.yahoo.vespa.athenz.tls.Pkcs10Csr; +import com.yahoo.vespa.athenz.tls.SignatureAlgorithm; +import com.yahoo.vespa.athenz.tls.X509CertificateBuilder; +import com.yahoo.vespa.athenz.tls.X509CertificateUtils; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.DERUTF8String; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.asn1.x500.AttributeTypeAndValue; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x500.style.BCStyle; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.Extensions; -import org.bouncycastle.cert.X509v3CertificateBuilder; -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.jcajce.JcaContentSignerBuilder; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; -import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; - -import java.math.BigInteger; + +import javax.security.auth.x500.X500Principal; import java.security.PrivateKey; -import java.security.Provider; -import java.security.PublicKey; import java.security.cert.X509Certificate; import java.time.Clock; import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; +import java.time.Instant; import java.util.List; import java.util.logging.Logger; import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig; @@ -56,16 +38,13 @@ public class CertificateSigner { private static final Logger log = Logger.getLogger(CertificateSigner.class.getName()); - static final String SIGNER_ALGORITHM = "SHA256withRSA"; + static final SignatureAlgorithm SIGNER_ALGORITHM = SignatureAlgorithm.SHA256_WITH_RSA; static final Duration CERTIFICATE_EXPIRATION = Duration.ofDays(30); - private static final List<ASN1ObjectIdentifier> ILLEGAL_EXTENSIONS = ImmutableList.of( - Extension.basicConstraints, Extension.subjectAlternativeName); - - private final JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter(); - private final Provider provider = new BouncyCastleProvider(); + private static final List<Extension> ILLEGAL_EXTENSIONS = ImmutableList.of( + Extension.BASIC_CONSTRAINS, Extension.SUBJECT_ALTERNATIVE_NAMES); private final PrivateKey caPrivateKey; - private final X500Name issuer; + private final X500Principal issuer; private final Clock clock; @Inject @@ -78,7 +57,7 @@ public class CertificateSigner { CertificateSigner(PrivateKey caPrivateKey, String loadBalancerAddress, Clock clock) { this.caPrivateKey = caPrivateKey; - this.issuer = new X500Name("CN=" + loadBalancerAddress); + this.issuer = new X500Principal("CN=" + loadBalancerAddress); this.clock = clock; } @@ -89,46 +68,28 @@ public class CertificateSigner { * <li>CSR does not contain any any of the extensions in {@code ILLEGAL_EXTENSIONS}</li> * </ul> */ - X509Certificate generateX509Certificate(PKCS10CertificationRequest certReq, String remoteHostname) { - verifyCertificateCommonName(certReq.getSubject(), remoteHostname); - verifyCertificateExtensions(certReq); - - Date notBefore = Date.from(clock.instant()); - Date notAfter = Date.from(clock.instant().plus(CERTIFICATE_EXPIRATION)); + X509Certificate generateX509Certificate(Pkcs10Csr csr, String remoteHostname) { + verifyCertificateCommonName(csr.getSubject(), remoteHostname); + verifyCertificateExtensions(csr); + Instant now = clock.instant(); try { - PublicKey publicKey = new JcaPKCS10CertificationRequest(certReq).getPublicKey(); - X509v3CertificateBuilder caBuilder = new JcaX509v3CertificateBuilder( - issuer, BigInteger.valueOf(clock.millis()), notBefore, notAfter, certReq.getSubject(), publicKey) - - // Set Basic Constraints to false - .addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); - - ContentSigner caSigner = new JcaContentSignerBuilder(SIGNER_ALGORITHM) - .setProvider(provider) - .build(caPrivateKey); - - return certificateConverter - .setProvider(provider) - .getCertificate(caBuilder.build(caSigner)); + return X509CertificateBuilder.fromCsr(csr, issuer, now, now.plus(CERTIFICATE_EXPIRATION), caPrivateKey, SIGNER_ALGORITHM, now.toEpochMilli()) + .setBasicConstraints(true, false) + .build(); } catch (Exception ex) { log.log(LogLevel.ERROR, "Failed to generate X509 Certificate", ex); - throw new RuntimeException("Failed to generate X509 Certificate"); + throw new RuntimeException("Failed to generate X509 Certificate", ex); } } - static void verifyCertificateCommonName(X500Name subject, String remoteHostname) { - List<AttributeTypeAndValue> attributesAndValues = Arrays.stream(subject.getRDNs()) - .flatMap(rdn -> rdn.isMultiValued() ? - Stream.of(rdn.getTypesAndValues()) : Stream.of(rdn.getFirst())) - .filter(attr -> attr.getType() == BCStyle.CN) - .collect(Collectors.toList()); - - if (attributesAndValues.size() != 1) { + static void verifyCertificateCommonName(X500Principal subject, String remoteHostname) { + List<String> commonNames = X509CertificateUtils.getCommonNames(subject); + if (commonNames.size() != 1) { throw new IllegalArgumentException("Only 1 common name should be set"); } - String actualCommonName = DERUTF8String.getInstance(attributesAndValues.get(0).getValue()).getString(); + String actualCommonName = commonNames.get(0); if (! actualCommonName.equals(remoteHostname)) { throw new IllegalArgumentException("Remote hostname " + remoteHostname + " does not match common name " + actualCommonName); @@ -136,15 +97,12 @@ public class CertificateSigner { } @SuppressWarnings("unchecked") - static void verifyCertificateExtensions(PKCS10CertificationRequest request) { - List<String> illegalExt = Arrays - .stream(request.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)) - .map(attribute -> Extensions.getInstance(attribute.getAttrValues().getObjectAt(0))) - .flatMap(ext -> Collections.list((Enumeration<ASN1ObjectIdentifier>) ext.oids()).stream()) - .filter(ILLEGAL_EXTENSIONS::contains) - .map(ASN1ObjectIdentifier::getId) + static void verifyCertificateExtensions(Pkcs10Csr csr) { + List<String> extensionOIds = csr.getExtensionOIds(); + List<String> illegalExt = ILLEGAL_EXTENSIONS.stream() + .map(Extension::getOId) + .filter(extensionOIds::contains) .collect(Collectors.toList()); - if (! illegalExt.isEmpty()) { throw new IllegalArgumentException("CSR contains illegal extensions: " + String.join(", ", illegalExt)); } diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java index 0c6199efdcb..1dd452866a5 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca; import com.google.inject.Inject; import com.yahoo.container.jaxrs.annotation.Component; import com.yahoo.log.LogLevel; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import com.yahoo.vespa.athenz.tls.Pkcs10Csr; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.BadRequestException; @@ -45,7 +45,7 @@ public class CertificateSignerResource { try { InetAddress addr = InetAddress.getByName(req.getRemoteAddr()); String remoteHostname = addr.getHostName(); - PKCS10CertificationRequest csr = csrPayload.csr; + Pkcs10Csr csr = csrPayload.csr; log.log(LogLevel.DEBUG, "Certification request from " + remoteHostname + ": " + csr); X509Certificate certificate = certificateSigner.generateX509Certificate(csr, remoteHostname); return new CertificateSerializedPayload(certificate); diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java index f56214513aa..375a4c3e17d 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java @@ -7,11 +7,10 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import com.yahoo.vespa.athenz.tls.Pkcs10Csr; +import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils; import java.io.IOException; -import java.io.StringReader; /** * Contains PEM formatted Certificate Signing Request (CSR) @@ -20,11 +19,11 @@ import java.io.StringReader; */ public class CsrSerializedPayload { - @JsonProperty("csr") public final PKCS10CertificationRequest csr; + @JsonProperty("csr") public final Pkcs10Csr csr; @JsonCreator public CsrSerializedPayload(@JsonProperty("csr") @JsonDeserialize(using = CertificateRequestDeserializer.class) - PKCS10CertificationRequest csr) { + Pkcs10Csr csr) { this.csr = csr; } @@ -50,13 +49,11 @@ public class CsrSerializedPayload { '}'; } - public static class CertificateRequestDeserializer extends JsonDeserializer<PKCS10CertificationRequest> { + public static class CertificateRequestDeserializer extends JsonDeserializer<Pkcs10Csr> { @Override - public PKCS10CertificationRequest deserialize( + public Pkcs10Csr deserialize( JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { - try (PEMParser pemParser = new PEMParser(new StringReader(jsonParser.getValueAsString()))) { - return (PKCS10CertificationRequest) pemParser.readObject(); - } + return Pkcs10CsrUtils.fromPem(jsonParser.getValueAsString()); } } } |