1package com.google.polo.ssl;
2
3import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
4import org.bouncycastle.asn1.x509.BasicConstraints;
5import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
6import org.bouncycastle.asn1.x509.GeneralName;
7import org.bouncycastle.asn1.x509.GeneralNames;
8import org.bouncycastle.asn1.x509.KeyPurposeId;
9import org.bouncycastle.asn1.x509.KeyUsage;
10import org.bouncycastle.asn1.x509.X509Extensions;
11import org.bouncycastle.asn1.x509.X509Name;
12import org.bouncycastle.x509.X509V3CertificateGenerator;
13import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
14import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
15
16import java.math.BigInteger;
17import java.security.GeneralSecurityException;
18import java.security.KeyPair;
19import java.security.PublicKey;
20import java.security.cert.X509Certificate;
21import java.util.Calendar;
22import java.util.Date;
23
24/**
25 * Utility class to generate X509 Root Certificates and Issue X509 Certificates signed by a root
26 * Certificate.
27 */
28public class CsrUtil {
29    private static final String SIGNATURE_ALGORITHM = "SHA256WithRSAEncryption";
30    private static final String EMAIL = "android-tv-remote-support@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