| 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.X509Extensions; |
| import org.bouncycastle.asn1.x509.X509Name; |
| import org.bouncycastle.x509.X509V3CertificateGenerator; |
| import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; |
| import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; |
| |
| 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, |
| new SubjectKeyIdentifierStructure(rootPair.getPublic())); |
| |
| 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, |
| new SubjectKeyIdentifierStructure(publicKey)); |
| // 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 }; |
| } |
| } |