blob: 8fb7e8a998f427cd98290e55246ff9ba3ed1ecbe [file] [log] [blame]
Tobias Thierer6c251e22016-06-24 19:04:17 +01001/*
2 * Copyright (C) 2016 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.squareup.okhttp.internal;
17
18import java.math.BigInteger;
19import java.security.GeneralSecurityException;
20import java.security.KeyPair;
21import java.security.KeyPairGenerator;
22import java.security.SecureRandom;
23import java.security.Security;
24import java.security.cert.X509Certificate;
25import java.util.Date;
26import java.util.UUID;
27import javax.security.auth.x500.X500Principal;
28import org.bouncycastle.asn1.x509.BasicConstraints;
29import org.bouncycastle.asn1.x509.X509Extensions;
30import org.bouncycastle.jce.provider.BouncyCastleProvider;
31import org.bouncycastle.x509.X509V3CertificateGenerator;
32
33/**
34 * A certificate and its private key. This can be used on the server side by HTTPS servers, or on
35 * the client side to verify those HTTPS servers. A held certificate can also be used to sign other
36 * held certificates, as done in practice by certificate authorities.
37 */
38public final class HeldCertificate {
39 public final X509Certificate certificate;
40 public final KeyPair keyPair;
41
42 public HeldCertificate(X509Certificate certificate, KeyPair keyPair) {
43 this.certificate = certificate;
44 this.keyPair = keyPair;
45 }
46
47 public static final class Builder {
48 static {
49 Security.addProvider(new BouncyCastleProvider());
50 }
51
52 private final long duration = 1000L * 60 * 60 * 24; // One day.
53 private String hostname;
54 private String serialNumber = "1";
55 private KeyPair keyPair;
56 private HeldCertificate issuedBy;
57 private int maxIntermediateCas;
58
59 public Builder serialNumber(String serialNumber) {
60 this.serialNumber = serialNumber;
61 return this;
62 }
63
64 /**
65 * Set this certificate's name. Typically this is the URL hostname for TLS certificates. This is
66 * the CN (common name) in the certificate. Will be a random string if no value is provided.
67 */
68 public Builder commonName(String hostname) {
69 this.hostname = hostname;
70 return this;
71 }
72
73 public Builder keyPair(KeyPair keyPair) {
74 this.keyPair = keyPair;
75 return this;
76 }
77
78 /**
79 * Set the certificate that signs this certificate. If unset, a self-signed certificate will be
80 * generated.
81 */
82 public Builder issuedBy(HeldCertificate signedBy) {
83 this.issuedBy = signedBy;
84 return this;
85 }
86
87 /**
88 * Set this certificate to be a certificate authority, with up to {@code maxIntermediateCas}
89 * intermediate certificate authorities beneath it.
90 */
91 public Builder ca(int maxIntermediateCas) {
92 this.maxIntermediateCas = maxIntermediateCas;
93 return this;
94 }
95
96 public HeldCertificate build() throws GeneralSecurityException {
97 // Subject, public & private keys for this certificate.
98 KeyPair heldKeyPair = keyPair != null
99 ? keyPair
100 : generateKeyPair();
101 X500Principal subject = hostname != null
102 ? new X500Principal("CN=" + hostname)
103 : new X500Principal("CN=" + UUID.randomUUID());
104
105 // Subject, public & private keys for this certificate's signer. It may be self signed!
106 KeyPair signedByKeyPair;
107 X500Principal signedByPrincipal;
108 if (issuedBy != null) {
109 signedByKeyPair = issuedBy.keyPair;
110 signedByPrincipal = issuedBy.certificate.getSubjectX500Principal();
111 } else {
112 signedByKeyPair = heldKeyPair;
113 signedByPrincipal = subject;
114 }
115
116 // Generate & sign the certificate.
117 long now = System.currentTimeMillis();
118 X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
119 generator.setSerialNumber(new BigInteger(serialNumber));
120 generator.setIssuerDN(signedByPrincipal);
121 generator.setNotBefore(new Date(now));
122 generator.setNotAfter(new Date(now + duration));
123 generator.setSubjectDN(subject);
124 generator.setPublicKey(heldKeyPair.getPublic());
125 generator.setSignatureAlgorithm("SHA256WithRSAEncryption");
126
127 if (maxIntermediateCas > 0) {
128 generator.addExtension(X509Extensions.BasicConstraints, true,
129 new BasicConstraints(maxIntermediateCas));
130 }
131
Adam Vartaniana17e2d02017-10-12 15:24:05 +0100132 // Android-changed: Use AndroidOpenSSL provider instead of BC.
Tobias Thierer6c251e22016-06-24 19:04:17 +0100133 X509Certificate certificate = generator.generateX509Certificate(
Adam Vartaniana17e2d02017-10-12 15:24:05 +0100134 signedByKeyPair.getPrivate(), "AndroidOpenSSL");
Tobias Thierer6c251e22016-06-24 19:04:17 +0100135 return new HeldCertificate(certificate, heldKeyPair);
136 }
137
138 public KeyPair generateKeyPair() throws GeneralSecurityException {
Adam Vartaniana17e2d02017-10-12 15:24:05 +0100139 // Android-changed: Don't specify provider for KeyPairGenerator instance.
140 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
Tobias Thierer6c251e22016-06-24 19:04:17 +0100141 keyPairGenerator.initialize(1024, new SecureRandom());
142 return keyPairGenerator.generateKeyPair();
143 }
144 }
145}