1 package com.google.polo.ssl; 2 3 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; 4 import org.bouncycastle.asn1.x509.BasicConstraints; 5 import org.bouncycastle.asn1.x509.ExtendedKeyUsage; 6 import org.bouncycastle.asn1.x509.GeneralName; 7 import org.bouncycastle.asn1.x509.GeneralNames; 8 import org.bouncycastle.asn1.x509.KeyPurposeId; 9 import org.bouncycastle.asn1.x509.KeyUsage; 10 import org.bouncycastle.asn1.x509.X509Extensions; 11 import org.bouncycastle.asn1.x509.X509Name; 12 import org.bouncycastle.x509.X509V3CertificateGenerator; 13 import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; 14 import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; 15 16 import java.math.BigInteger; 17 import java.security.GeneralSecurityException; 18 import java.security.KeyPair; 19 import java.security.PublicKey; 20 import java.security.cert.X509Certificate; 21 import java.util.Calendar; 22 import java.util.Date; 23 24 /** 25 * Utility class to generate X509 Root Certificates and Issue X509 Certificates signed by a root 26 * Certificate. 27 */ 28 public class CsrUtil { 29 private static final String SIGNATURE_ALGORITHM = "SHA256WithRSAEncryption"; 30 private static final String EMAIL = "android-tv-remote-support (at) google.com"; 31 private static final int NOT_BEFORE_NUMBER_OF_DAYS = -30; 32 private static final int NOT_AFTER_NUMBER_OF_DAYS = 10 * 365; 33 34 /** 35 * Generate a X509 Certificate that should be used as an authority/root certificate only. 36 * 37 * This certificate shouldn't be used for communications, only as an authority as it won't have 38 * the correct flags. 39 * 40 * @param rootName Common Name used in certificate. 41 * @param rootPair Key Pair used to signed the certificate 42 * @return 43 * @throws GeneralSecurityException 44 */ 45 public static X509Certificate generateX509V3AuthorityCertificate(String rootName, 46 KeyPair rootPair) 47 throws GeneralSecurityException { 48 Calendar calendar = Calendar.getInstance(); 49 calendar.add(Calendar.DAY_OF_YEAR, NOT_BEFORE_NUMBER_OF_DAYS); 50 Date notBefore = new Date(calendar.getTimeInMillis()); 51 calendar.add(Calendar.DAY_OF_YEAR, NOT_AFTER_NUMBER_OF_DAYS); 52 Date notAfter = new Date(calendar.getTimeInMillis()); 53 54 BigInteger serialNumber = BigInteger.valueOf(Math.abs(System.currentTimeMillis())); 55 56 return generateX509V3AuthorityCertificate(rootName, rootPair, notBefore, notAfter, serialNumber); 57 } 58 59 60 @SuppressWarnings("deprecation") 61 static X509Certificate generateX509V3AuthorityCertificate(String rootName, 62 KeyPair rootPair, Date notBefore, Date notAfter, BigInteger serialNumber) 63 throws GeneralSecurityException { 64 X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); 65 X509Name dnName = new X509Name(rootName); 66 67 certGen.setSerialNumber(serialNumber); 68 certGen.setIssuerDN(dnName); 69 certGen.setSubjectDN(dnName); 70 certGen.setNotBefore(notBefore); 71 certGen.setNotAfter(notAfter); 72 certGen.setPublicKey(rootPair.getPublic()); 73 certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM); 74 75 certGen.addExtension(X509Extensions.BasicConstraints, true, 76 new BasicConstraints(0)); 77 78 certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature 79 | KeyUsage.keyEncipherment | KeyUsage.keyCertSign)); 80 81 AuthorityKeyIdentifier authIdentifier = SslUtil.createAuthorityKeyIdentifier( 82 rootPair.getPublic(), dnName, serialNumber); 83 84 certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, true, authIdentifier); 85 certGen.addExtension(X509Extensions.SubjectKeyIdentifier, true, 86 new SubjectKeyIdentifierStructure(rootPair.getPublic())); 87 88 certGen.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames( 89 new GeneralName(GeneralName.rfc822Name, EMAIL))); 90 91 X509Certificate cert = certGen.generate(rootPair.getPrivate()); 92 return cert; 93 } 94 95 96 /** 97 * Given a public key and an authority certificate and key pair, issue an X509 Certificate 98 * chain signed by the provided authority certificate. 99 * 100 * @param name Common name used in the issued certificate. 101 * @param publicKey Public key to use in issued certificate. 102 * @param rootCert Root certificate used to issue the new certificate. 103 * @param rootPair Root key pair used to issue the new certificate. 104 * @return Array containing the issued certificate and the provided root certificate. 105 * @throws GeneralSecurityException 106 */ 107 public static X509Certificate[] issueX509V3Certificate(String name, PublicKey publicKey, 108 X509Certificate rootCert, KeyPair rootPair) throws GeneralSecurityException { 109 Calendar calendar = Calendar.getInstance(); 110 calendar.add(Calendar.DAY_OF_YEAR, NOT_BEFORE_NUMBER_OF_DAYS); 111 Date notBefore = new Date(calendar.getTimeInMillis()); 112 calendar.add(Calendar.DAY_OF_YEAR, NOT_AFTER_NUMBER_OF_DAYS); 113 Date notAfter = new Date(calendar.getTimeInMillis()); 114 115 BigInteger serialNumber = BigInteger.valueOf(Math.abs(System.currentTimeMillis())); 116 117 return issueX509V3Certificate(name, publicKey, rootCert, rootPair, notBefore, notAfter, serialNumber); 118 } 119 120 @SuppressWarnings("deprecation") 121 static X509Certificate[] issueX509V3Certificate(String name, PublicKey publicKey, 122 X509Certificate rootCert, KeyPair rootPair, Date notBefore, Date notAfter, 123 BigInteger serialNumber) throws GeneralSecurityException { 124 125 X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); 126 127 X509Name dnName = new X509Name(name); 128 129 certGen.setSerialNumber(serialNumber); 130 certGen.setIssuerDN(rootCert.getSubjectX500Principal()); 131 certGen.setNotBefore(notBefore); 132 certGen.setNotAfter(notAfter); 133 certGen.setSubjectDN(dnName); 134 certGen.setPublicKey(publicKey); 135 certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM); 136 137 // Use Root Certificate as the authority 138 certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, 139 new AuthorityKeyIdentifierStructure(rootCert)); 140 // Use provided public key for the subject 141 certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, 142 new SubjectKeyIdentifierStructure(publicKey)); 143 // This is not a CA certificate, do not allow 144 certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); 145 // This can be used for signature and encryption 146 certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature 147 | KeyUsage.keyEncipherment)); 148 // This is used for server authentication 149 certGen.addExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage( 150 KeyPurposeId.id_kp_serverAuth)); 151 152 X509Certificate issuedCert = certGen.generate(rootPair.getPrivate()); 153 154 return new X509Certificate[] { issuedCert, rootCert }; 155 } 156 } 157