Java tutorial
/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2011 mawoki@ymail.com * * 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 org.computerist.ssltools.zap; import java.io.IOException; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.SignatureException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.util.Date; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.X509Extension; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; /** * This is a singleton class. Use {@link #getService()} method to obtain a * service bean. This implementation is totally unbuffered and creates every * time you call {@link #createCertForHost(String)} a new certificate. If you * want to have a cached solution, have a look at * {@link CachedSslCertifificateServiceImpl}. This class is designed to be * thread safe. * * A word about serial numbers ... There have to be different serial numbers * generated, cause if multiple certificates with different finger prints do * have the same serial from the same CA, the browser gets crazy. At least, * Firefox v3.x does. * * @author MaWoKi * @see AttrCertExample how to manage CAs and stuff * @see CachedSslCertifificateServiceImpl for a cached SslCertificateService */ class FixedSslCertificateService { public static char[] PASSPHRASE = "0w45P.Z4p".toCharArray(); static String ZAPROXY_JKS_ALIAS = "owasp_zap_root_ca"; private X509Certificate caCert = null; private PublicKey caPubKey = null; private PrivateKey caPrivKey = null; private final AtomicLong serial; private KeyStore userKs; private static final FixedSslCertificateService singleton = new FixedSslCertificateService(); private FixedSslCertificateService() { Security.addProvider(new BouncyCastleProvider()); final Random rnd = new Random(); rnd.setSeed(System.currentTimeMillis()); // prevent browser certificate caches, cause of doubled serial numbers // using 48bit random number long sl = ((long) rnd.nextInt()) << 32 | (rnd.nextInt() & 0xFFFFFFFFL); // let reserve of 16 bit for increasing, serials have to be positive sl = sl & 0x0000FFFFFFFFFFFFL; this.serial = new AtomicLong(sl); try { userKs = KeyStore.getInstance("JKS"); userKs.load(null, null); } catch (Exception e) { e.printStackTrace(); } } public synchronized void initializeRootCA(KeyStore keystore) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { if (keystore == null) { this.caCert = null; this.caPrivKey = null; this.caPubKey = null; } else { this.caCert = (X509Certificate) keystore.getCertificate(ZAPROXY_JKS_ALIAS); this.caPrivKey = (RSAPrivateKey) keystore.getKey(ZAPROXY_JKS_ALIAS, PASSPHRASE); this.caPubKey = this.caCert.getPublicKey(); } } public KeyStore createCertForHost(String hostname) throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, NoSuchProviderException, SignatureException, KeyStoreException, IOException, UnrecoverableKeyException { if (hostname == null) { throw new IllegalArgumentException("Error, 'hostname' is not allowed to be null!"); } if (this.caCert == null || this.caPrivKey == null || this.caPubKey == null) { throw new RuntimeException( this.getClass() + " wasn't initialized! Got to options 'Dynamic SSL Certs' and create one."); } final KeyPair mykp = this.createKeyPair(); final PrivateKey privKey = mykp.getPrivate(); final PublicKey pubKey = mykp.getPublic(); X500NameBuilder namebld = new X500NameBuilder(BCStyle.INSTANCE); namebld.addRDN(BCStyle.CN, hostname); namebld.addRDN(BCStyle.OU, "Zed Attack Proxy Project"); namebld.addRDN(BCStyle.O, "OWASP"); namebld.addRDN(BCStyle.C, "xx"); namebld.addRDN(BCStyle.EmailAddress, "owasp-zed-attack-proxy@lists.owasp.org"); X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder( new X509CertificateHolder(caCert.getEncoded()).getSubject(), BigInteger.valueOf(serial.getAndIncrement()), new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30), new Date(System.currentTimeMillis() + 100 * (1000L * 60 * 60 * 24 * 30)), namebld.build(), pubKey); certGen.addExtension(X509Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(pubKey)); certGen.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false)); ContentSigner sigGen; try { sigGen = new JcaContentSignerBuilder("SHA1WithRSAEncryption").setProvider("BC").build(caPrivKey); } catch (OperatorCreationException e) { throw new CertificateException(e); } final X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC") .getCertificate(certGen.build(sigGen)); cert.checkValidity(new Date()); cert.verify(caPubKey); final Certificate[] chain = new Certificate[2]; chain[1] = this.caCert; chain[0] = cert; userKs.setKeyEntry(hostname, privKey, PASSPHRASE, chain); return userKs; } /** * Generates an 1024 bit RSA key pair using SHA1PRNG. * * Thoughts: 2048 takes much longer time on older CPUs. And for almost every * client, 1024 is sufficient. * * @return * @throws NoSuchAlgorithmException */ private KeyPair createKeyPair() throws NoSuchAlgorithmException { final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); final SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.setSeed(Long.toString(System.currentTimeMillis()).getBytes()); keyGen.initialize(1024, random); final KeyPair keypair = keyGen.generateKeyPair(); return keypair; } /** * @return return the current {@link SslCertificateService} */ public static FixedSslCertificateService getService() { return singleton; } public KeyStore getHostKeyStore() { return this.userKs; } }