package com.google.polo.ssl; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.X509Extensions; import org.bouncycastle.asn1.x509.X509Name; import org.bouncycastle.x509.X509V3CertificateGenerator; import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.Calendar; import java.util.Date; /** * Utility class to generate X509 Root Certificates and Issue X509 Certificates signed by a root * Certificate. */ public class CsrUtil { private static final String SIGNATURE_ALGORITHM = "SHA256WithRSAEncryption"; private static final String EMAIL = "android-tv-remote-support@google.com"; private static final int NOT_BEFORE_NUMBER_OF_DAYS = -30; private static final int NOT_AFTER_NUMBER_OF_DAYS = 10 * 365; /** * Generate a X509 Certificate that should be used as an authority/root certificate only. * * This certificate shouldn't be used for communications, only as an authority as it won't have * the correct flags. * * @param rootName Common Name used in certificate. * @param rootPair Key Pair used to signed the certificate * @return * @throws GeneralSecurityException */ public static X509Certificate generateX509V3AuthorityCertificate(String rootName, KeyPair rootPair) throws GeneralSecurityException { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR, NOT_BEFORE_NUMBER_OF_DAYS); Date notBefore = new Date(calendar.getTimeInMillis()); calendar.add(Calendar.DAY_OF_YEAR, NOT_AFTER_NUMBER_OF_DAYS); Date notAfter = new Date(calendar.getTimeInMillis()); BigInteger serialNumber = BigInteger.valueOf(Math.abs(System.currentTimeMillis())); return generateX509V3AuthorityCertificate(rootName, rootPair, notBefore, notAfter, serialNumber); } @SuppressWarnings("deprecation") static X509Certificate generateX509V3AuthorityCertificate(String rootName, KeyPair rootPair, Date notBefore, Date notAfter, BigInteger serialNumber) throws GeneralSecurityException { X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); X509Name dnName = new X509Name(rootName); certGen.setSerialNumber(serialNumber); certGen.setIssuerDN(dnName); certGen.setSubjectDN(dnName); certGen.setNotBefore(notBefore); certGen.setNotAfter(notAfter); certGen.setPublicKey(rootPair.getPublic()); certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM); certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(0)); certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.keyCertSign)); AuthorityKeyIdentifier authIdentifier = SslUtil.createAuthorityKeyIdentifier( rootPair.getPublic(), dnName, serialNumber); certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, true, authIdentifier); certGen.addExtension(X509Extensions.SubjectKeyIdentifier, true, SubjectKeyIdentifier.getInstance(rootPair.getPublic().getEncoded())); certGen.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames( new GeneralName(GeneralName.rfc822Name, EMAIL))); X509Certificate cert = certGen.generate(rootPair.getPrivate()); return cert; } /** * Given a public key and an authority certificate and key pair, issue an X509 Certificate * chain signed by the provided authority certificate. * * @param name Common name used in the issued certificate. * @param publicKey Public key to use in issued certificate. * @param rootCert Root certificate used to issue the new certificate. * @param rootPair Root key pair used to issue the new certificate. * @return Array containing the issued certificate and the provided root certificate. * @throws GeneralSecurityException */ public static X509Certificate[] issueX509V3Certificate(String name, PublicKey publicKey, X509Certificate rootCert, KeyPair rootPair) throws GeneralSecurityException { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR, NOT_BEFORE_NUMBER_OF_DAYS); Date notBefore = new Date(calendar.getTimeInMillis()); calendar.add(Calendar.DAY_OF_YEAR, NOT_AFTER_NUMBER_OF_DAYS); Date notAfter = new Date(calendar.getTimeInMillis()); BigInteger serialNumber = BigInteger.valueOf(Math.abs(System.currentTimeMillis())); return issueX509V3Certificate(name, publicKey, rootCert, rootPair, notBefore, notAfter, serialNumber); } @SuppressWarnings("deprecation") static X509Certificate[] issueX509V3Certificate(String name, PublicKey publicKey, X509Certificate rootCert, KeyPair rootPair, Date notBefore, Date notAfter, BigInteger serialNumber) throws GeneralSecurityException { X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); X509Name dnName = new X509Name(name); certGen.setSerialNumber(serialNumber); certGen.setIssuerDN(rootCert.getSubjectX500Principal()); certGen.setNotBefore(notBefore); certGen.setNotAfter(notAfter); certGen.setSubjectDN(dnName); certGen.setPublicKey(publicKey); certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM); // Use Root Certificate as the authority certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(rootCert)); // Use provided public key for the subject certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, SubjectKeyIdentifier.getInstance(publicKey.getEncoded())); // This is not a CA certificate, do not allow certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); // This can be used for signature and encryption certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); // This is used for server authentication certGen.addExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage( KeyPurposeId.id_kp_serverAuth)); X509Certificate issuedCert = certGen.generate(rootPair.getPrivate()); return new X509Certificate[] { issuedCert, rootCert }; } }