/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.brooklyn.util.core.crypto; import java.math.BigInteger; import java.security.KeyPair; import java.security.PublicKey; import java.security.SecureRandom; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.Date; import javax.security.auth.x500.X500Principal; import org.apache.brooklyn.core.internal.BrooklynInitialization; import org.apache.brooklyn.util.exceptions.Exceptions; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.X509Extension; import org.bouncycastle.jce.X509Principal; /** A fluent API which simplifies generating certificates (signed keys) */ /* NB - re deprecation - we use deprecated X509V3CertificateGenerator still * because the official replacement, X509v3CertificateBuilder, * drags in an add'l dependency (bcmail) and is harder to use. */ public class FluentKeySigner { static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); } protected X500Principal issuerPrincipal; protected KeyPair issuerKey; protected SecureRandom srand = new SecureRandom(); protected Date validityStartDate, validityEndDate; protected BigInteger serialNumber; protected String signatureAlgorithm = "MD5WithRSAEncryption"; protected AuthorityKeyIdentifier authorityKeyIdentifier; protected X509Certificate authorityCertificate; public FluentKeySigner(X500Principal issuerPrincipal, KeyPair issuerKey) { this.issuerPrincipal = issuerPrincipal; this.issuerKey = issuerKey; validFromDaysAgo(7); validForYears(10); } public FluentKeySigner(String issuerCommonName, KeyPair issuerKey) { this(SecureKeys.getX500PrincipalWithCommonName(issuerCommonName), issuerKey); } public FluentKeySigner(String issuerCommonName) { this(issuerCommonName, SecureKeys.newKeyPair()); } public FluentKeySigner(X509Certificate caCert, KeyPair caKey) { this(caCert.getIssuerX500Principal(), caKey); authorityCertificate(caCert); } public KeyPair getKey() { return issuerKey; } public X500Principal getPrincipal() { return issuerPrincipal; } @SuppressWarnings("deprecation") public String getCommonName() { // TODO see deprecation note at top of file // for modernising, would RFC4519Style.cn work ? return (String) new X509Principal(issuerPrincipal.getName()).getValues(org.bouncycastle.asn1.x509.X509Name.CN).elementAt(0); } public X509Certificate getAuthorityCertificate() { return authorityCertificate; } public FluentKeySigner validFromDaysAgo(long days) { return validFrom(new Date( (System.currentTimeMillis() / (1000L*60*60*24) - days) * 1000L*60*60*24)); } public FluentKeySigner validFrom(Date d) { validityStartDate = d; return this; } public FluentKeySigner validForYears(long years) { return validUntil(new Date( (System.currentTimeMillis() / (1000L*60*60*24) + 365*years) * 1000L*60*60*24)); } public FluentKeySigner validUntil(Date d) { validityEndDate = d; return this; } /** use a hard-coded serial number; or make one up, if null */ public FluentKeySigner serialNumber(BigInteger serialNumber) { this.serialNumber = serialNumber; return this; } public FluentKeySigner signatureAlgorithm(String signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; return this; } @SuppressWarnings("deprecation") public FluentKeySigner authorityCertificate(X509Certificate certificate) { try { authorityKeyIdentifier(new org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure(certificate)); this.authorityCertificate = certificate; return this; } catch (CertificateParsingException e) { throw Exceptions.propagate(e); } } public FluentKeySigner authorityKeyIdentifier(AuthorityKeyIdentifier authorityKeyIdentifier) { this.authorityKeyIdentifier = authorityKeyIdentifier; return this; } public FluentKeySigner selfsign() { if (authorityCertificate!=null) throw new IllegalStateException("Signer already has certificate"); authorityCertificate(newCertificateFor(getCommonName(), getKey())); return this; } // TODO see note re deprecation at start of file @SuppressWarnings("deprecation") public X509Certificate newCertificateFor(X500Principal subject, PublicKey keyToCertify) { try { org.bouncycastle.x509.X509V3CertificateGenerator v3CertGen = new org.bouncycastle.x509.X509V3CertificateGenerator(); v3CertGen.setSerialNumber( serialNumber != null ? serialNumber : // must be positive BigInteger.valueOf(srand.nextLong()).abs().add(BigInteger.ONE)); v3CertGen.setIssuerDN(issuerPrincipal); v3CertGen.setNotBefore(validityStartDate); v3CertGen.setNotAfter(validityEndDate); v3CertGen.setSignatureAlgorithm(signatureAlgorithm); v3CertGen.setSubjectDN(subject); v3CertGen.setPublicKey(keyToCertify); v3CertGen.addExtension(X509Extension.subjectKeyIdentifier, false, new org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure(keyToCertify)); if (authorityKeyIdentifier!=null) v3CertGen.addExtension(X509Extension.authorityKeyIdentifier, false, authorityKeyIdentifier); X509Certificate pkCertificate = v3CertGen.generate(issuerKey.getPrivate(), "BC"); return pkCertificate; } catch (Exception e) { throw Exceptions.propagate(e); } } public X509Certificate newCertificateFor(String commonName, PublicKey key) { // SecureKeys.getX509PrincipalWithCommonName(commonName) return newCertificateFor( SecureKeys.getX500PrincipalWithCommonName(commonName) // new X509Principal("CN=" + commonName + ", OU=None, O=None, L=None, C=None") , key); } public X509Certificate newCertificateFor(String commonName, KeyPair key) { return newCertificateFor(commonName, key.getPublic()); } }