| /* |
| * Copyright (C) 2016 Square, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.squareup.okhttp.internal; |
| |
| import java.math.BigInteger; |
| import java.security.GeneralSecurityException; |
| import java.security.KeyPair; |
| import java.security.KeyPairGenerator; |
| import java.security.SecureRandom; |
| import java.security.Security; |
| import java.security.cert.X509Certificate; |
| import java.util.Date; |
| import java.util.UUID; |
| import javax.security.auth.x500.X500Principal; |
| import org.bouncycastle.asn1.x509.BasicConstraints; |
| import org.bouncycastle.asn1.x509.X509Extensions; |
| import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| import org.bouncycastle.x509.X509V3CertificateGenerator; |
| |
| /** |
| * A certificate and its private key. This can be used on the server side by HTTPS servers, or on |
| * the client side to verify those HTTPS servers. A held certificate can also be used to sign other |
| * held certificates, as done in practice by certificate authorities. |
| */ |
| public final class HeldCertificate { |
| public final X509Certificate certificate; |
| public final KeyPair keyPair; |
| |
| public HeldCertificate(X509Certificate certificate, KeyPair keyPair) { |
| this.certificate = certificate; |
| this.keyPair = keyPair; |
| } |
| |
| public static final class Builder { |
| static { |
| Security.addProvider(new BouncyCastleProvider()); |
| } |
| |
| private final long duration = 1000L * 60 * 60 * 24; // One day. |
| private String hostname; |
| private String serialNumber = "1"; |
| private KeyPair keyPair; |
| private HeldCertificate issuedBy; |
| private int maxIntermediateCas; |
| |
| public Builder serialNumber(String serialNumber) { |
| this.serialNumber = serialNumber; |
| return this; |
| } |
| |
| /** |
| * Set this certificate's name. Typically this is the URL hostname for TLS certificates. This is |
| * the CN (common name) in the certificate. Will be a random string if no value is provided. |
| */ |
| public Builder commonName(String hostname) { |
| this.hostname = hostname; |
| return this; |
| } |
| |
| public Builder keyPair(KeyPair keyPair) { |
| this.keyPair = keyPair; |
| return this; |
| } |
| |
| /** |
| * Set the certificate that signs this certificate. If unset, a self-signed certificate will be |
| * generated. |
| */ |
| public Builder issuedBy(HeldCertificate signedBy) { |
| this.issuedBy = signedBy; |
| return this; |
| } |
| |
| /** |
| * Set this certificate to be a certificate authority, with up to {@code maxIntermediateCas} |
| * intermediate certificate authorities beneath it. |
| */ |
| public Builder ca(int maxIntermediateCas) { |
| this.maxIntermediateCas = maxIntermediateCas; |
| return this; |
| } |
| |
| public HeldCertificate build() throws GeneralSecurityException { |
| // Subject, public & private keys for this certificate. |
| KeyPair heldKeyPair = keyPair != null |
| ? keyPair |
| : generateKeyPair(); |
| X500Principal subject = hostname != null |
| ? new X500Principal("CN=" + hostname) |
| : new X500Principal("CN=" + UUID.randomUUID()); |
| |
| // Subject, public & private keys for this certificate's signer. It may be self signed! |
| KeyPair signedByKeyPair; |
| X500Principal signedByPrincipal; |
| if (issuedBy != null) { |
| signedByKeyPair = issuedBy.keyPair; |
| signedByPrincipal = issuedBy.certificate.getSubjectX500Principal(); |
| } else { |
| signedByKeyPair = heldKeyPair; |
| signedByPrincipal = subject; |
| } |
| |
| // Generate & sign the certificate. |
| long now = System.currentTimeMillis(); |
| X509V3CertificateGenerator generator = new X509V3CertificateGenerator(); |
| generator.setSerialNumber(new BigInteger(serialNumber)); |
| generator.setIssuerDN(signedByPrincipal); |
| generator.setNotBefore(new Date(now)); |
| generator.setNotAfter(new Date(now + duration)); |
| generator.setSubjectDN(subject); |
| generator.setPublicKey(heldKeyPair.getPublic()); |
| generator.setSignatureAlgorithm("SHA256WithRSAEncryption"); |
| |
| if (maxIntermediateCas > 0) { |
| generator.addExtension(X509Extensions.BasicConstraints, true, |
| new BasicConstraints(maxIntermediateCas)); |
| } |
| |
| // Android-changed: Use AndroidOpenSSL provider instead of BC. |
| X509Certificate certificate = generator.generateX509Certificate( |
| signedByKeyPair.getPrivate(), "AndroidOpenSSL"); |
| return new HeldCertificate(certificate, heldKeyPair); |
| } |
| |
| public KeyPair generateKeyPair() throws GeneralSecurityException { |
| // Android-changed: Don't specify provider for KeyPairGenerator instance. |
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); |
| keyPairGenerator.initialize(1024, new SecureRandom()); |
| return keyPairGenerator.generateKeyPair(); |
| } |
| } |
| } |