summaryrefslogtreecommitdiff
path: root/java/src/com/google/polo/ssl/CsrUtil.java
blob: d5ff7bc316d772443c93cc6c78a0e77ba2083660 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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 };
    }
}