Skip to content

Commit

Permalink
Fix for zaproxy#3959. Create valid cert for an SSLEndpoint accessed i…
Browse files Browse the repository at this point in the history
…n the browser by an IpAddress. Added the IpAddress to the cert as SubjectAlternativeName.
  • Loading branch information
denniskniep committed Oct 23, 2017
1 parent b4dac6d commit ade79e4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 24 deletions.
22 changes: 13 additions & 9 deletions src/org/parosproxy/paros/network/SSLConnector.java
Expand Up @@ -32,6 +32,7 @@
// ZAP: 2015/10/13 Issue 1975: Allow use of default disabled cipher suites (such as RC4-SHA)
// ZAP: 2017/04/14 Validate that SSLv2Hello is set in conjunction with at least one SSL/TLS version.
// ZAP: 2017/09/22 Rely on SNI if the domain is no known when creating the SSL/TLS tunnel.
// ZAP: 2017/10/23 Pass the listeningIpAddress when creating the SSL/TLS tunnel.

package org.parosproxy.paros.network;

Expand Down Expand Up @@ -530,7 +531,8 @@ public Socket createSocket(Socket socket, String host, int port,
*/
public Socket createTunnelServerSocket(String targethost, Socket socket) throws IOException {
// ZAP: added host name parameter
SSLSocket s = (SSLSocket) getTunnelSSLSocketFactory(targethost).createSocket(socket, socket
InetAddress listeningAddress = socket.getInetAddress();
SSLSocket s = (SSLSocket) getTunnelSSLSocketFactory(targethost, listeningAddress).createSocket(socket, socket
.getInetAddress().getHostAddress(), socket.getPort(), true);

s.setUseClientMode(false);
Expand All @@ -539,7 +541,7 @@ public Socket createTunnelServerSocket(String targethost, Socket socket) throws
}

// ZAP: added new ServerSocketFaktory with support of dynamic SSL certificates
public SSLSocketFactory getTunnelSSLSocketFactory(String hostname) {
public SSLSocketFactory getTunnelSSLSocketFactory(String hostname, InetAddress listeningAddress) {

// SSLServerSocketFactory ssf = null;
// set up key manager to do server authentication
Expand All @@ -552,10 +554,10 @@ public SSLSocketFactory getTunnelSSLSocketFactory(String hostname) {

KeyManager[] keyManagers;
if (hostname != null && !hostname.isEmpty()) {
initKeyManagerFactoryWithCertForHostname(kmf, hostname);
initKeyManagerFactoryWithCertForHostname(kmf, hostname, listeningAddress);
keyManagers = kmf.getKeyManagers();
} else {
keyManagers = new KeyManager[] { new SniX509KeyManager(kmf) };
keyManagers = new KeyManager[] { new SniX509KeyManager(kmf, listeningAddress) };
}

java.security.SecureRandom x = new java.security.SecureRandom();
Expand All @@ -576,10 +578,10 @@ public SSLSocketFactory getTunnelSSLSocketFactory(String hostname) {
}
}

static void initKeyManagerFactoryWithCertForHostname(KeyManagerFactory keyManagerFactory, String hostname)
static void initKeyManagerFactoryWithCertForHostname(KeyManagerFactory keyManagerFactory, String hostname, InetAddress listeningAddress)
throws InvalidKeyException, UnrecoverableKeyException, NoSuchAlgorithmException, CertificateException,
NoSuchProviderException, SignatureException, KeyStoreException, IOException {
KeyStore ks = CachedSslCertifificateServiceImpl.getService().createCertForHost(hostname);
KeyStore ks = CachedSslCertifificateServiceImpl.getService().createCertForHost(hostname, listeningAddress);
keyManagerFactory.init(ks, SslCertificateService.PASSPHRASE);
}

Expand Down Expand Up @@ -650,10 +652,12 @@ public boolean isStale(long currentTime) {
private static class SniX509KeyManager implements X509KeyManager {

private final KeyManagerFactory keyManagerFactory;
private InetAddress listeningAddress;
private X509KeyManager x509KeyManager;

public SniX509KeyManager(KeyManagerFactory keyManagerFactory) {
public SniX509KeyManager(KeyManagerFactory keyManagerFactory, InetAddress listeningAddress) {
this.keyManagerFactory = keyManagerFactory;
this.listeningAddress = listeningAddress;
}

@Override
Expand All @@ -673,11 +677,11 @@ private void createX509KeyManager(Socket socket) {
String hostname = extractHostname(sslSocket.getHandshakeSession());

if (hostname == null) {
logAndThrow("No domain extracted from SSL/TLS handshake session.");
logger.info("No domain extracted from SSL/TLS handshake session.");
}

try {
initKeyManagerFactoryWithCertForHostname(keyManagerFactory, hostname);
initKeyManagerFactoryWithCertForHostname(keyManagerFactory, hostname, listeningAddress);
} catch (InvalidKeyException
| UnrecoverableKeyException
| NoSuchAlgorithmException
Expand Down
Expand Up @@ -20,6 +20,7 @@
package org.parosproxy.paros.security;

import java.io.IOException;
import java.net.InetAddress;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
Expand Down Expand Up @@ -51,18 +52,24 @@ private CachedSslCertifificateServiceImpl() {
private Map<String, KeyStore> cache = new HashMap<>();

@Override
public synchronized KeyStore createCertForHost(String hostname)
public synchronized KeyStore createCertForHost(String hostname, InetAddress listeningAddress)
throws NoSuchAlgorithmException, InvalidKeyException,
CertificateException, NoSuchProviderException, SignatureException,
KeyStoreException, IOException, UnrecoverableKeyException {
if (this.cache.containsKey(hostname)) {
return this.cache.get(hostname);
String key = getKey(hostname, listeningAddress);
if (this.cache.containsKey(key)) {
return this.cache.get(key);
}
final KeyStore ks = delegate.createCertForHost(hostname);
this.cache.put(hostname, ks);
final KeyStore ks = delegate.createCertForHost(hostname, listeningAddress);
this.cache.put(key, ks);
return ks;
}

private String getKey(String hostname, InetAddress listeningAddress) {
String keyForCache = String.format("Host:%s;InetAddress:%s", hostname, listeningAddress.getHostAddress());
return keyForCache;
}

/**
* @return return the current {@link SslCertificateService}
*/
Expand Down
4 changes: 3 additions & 1 deletion src/org/parosproxy/paros/security/SslCertificateService.java
Expand Up @@ -20,6 +20,7 @@
package org.parosproxy.paros.security;

import java.io.IOException;
import java.net.InetAddress;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
Expand Down Expand Up @@ -51,6 +52,7 @@ public interface SslCertificateService {
* {@link KeyStore} available with alias {@link #ZAPROXY_JKS_ALIAS}.
*
* @param hostname
* @param listeningAddress
* @return a {@link KeyStore} which contains root certificate, signed
* certificate, private key and public key of signed certificate
* @throws NoSuchAlgorithmException
Expand All @@ -63,7 +65,7 @@ public interface SslCertificateService {
* @throws UnrecoverableKeyException
* @throws MissingRootCertificateException when it wasn't initialized.
*/
KeyStore createCertForHost(String hostname)
KeyStore createCertForHost(String hostname, InetAddress listeningAddress)
throws NoSuchAlgorithmException, InvalidKeyException,
CertificateException, NoSuchProviderException, SignatureException,
KeyStoreException, IOException, UnrecoverableKeyException;
Expand Down
35 changes: 26 additions & 9 deletions src/org/parosproxy/paros/security/SslCertificateServiceImpl.java
Expand Up @@ -21,6 +21,7 @@

import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
Expand All @@ -38,6 +39,7 @@
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
Expand All @@ -61,7 +63,7 @@
/**
* 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.
* every time you call {@link SslCertificateService#createCertForHost(String, InetAddress)} 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.
*
Expand Down Expand Up @@ -110,11 +112,7 @@ public synchronized void initializeRootCA(KeyStore keystore) throws KeyStoreExce
}

@Override
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!");
}
public KeyStore createCertForHost(String hostname, InetAddress listeningAddress) throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, NoSuchProviderException, SignatureException, KeyStoreException, IOException, UnrecoverableKeyException {

if (this.caCert == null || this.caPrivKey == null || this.caPubKey == null) {
throw new MissingRootCertificateException(this.getClass() + " wasn't initialized! Got to options 'Dynamic SSL Certs' and create one.");
Expand All @@ -124,8 +122,10 @@ public KeyStore createCertForHost(String hostname) throws NoSuchAlgorithmExcepti
final PrivateKey privKey = mykp.getPrivate();
final PublicKey pubKey = mykp.getPublic();

X500NameBuilder namebld = new X500NameBuilder(BCStyle.INSTANCE);
namebld.addRDN(BCStyle.CN, hostname);
X500NameBuilder namebld = new X500NameBuilder(BCStyle.INSTANCE);
if (hostname != null) {
namebld.addRDN(BCStyle.CN, hostname);
}
namebld.addRDN(BCStyle.OU, "Zed Attack Proxy Project");
namebld.addRDN(BCStyle.O, "OWASP");
namebld.addRDN(BCStyle.C, "xx");
Expand All @@ -142,7 +142,24 @@ public KeyStore createCertForHost(String hostname) throws NoSuchAlgorithmExcepti

certGen.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(pubKey.getEncoded()));
certGen.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
certGen.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(new GeneralName(GeneralName.dNSName, hostname)));

ArrayList<GeneralName> subjectAlternativeNames = new ArrayList<>();
boolean subjectAlternativeNamesIsCritical = true;
if (hostname != null) {
subjectAlternativeNames.add(new GeneralName(GeneralName.dNSName, hostname));
subjectAlternativeNamesIsCritical = false;
}

if(!listeningAddress.isAnyLocalAddress()){
subjectAlternativeNames.add(new GeneralName(GeneralName.iPAddress, listeningAddress.getHostAddress()));
}

if (hostname == null && listeningAddress.isAnyLocalAddress()) {
throw new IllegalArgumentException("Error, 'hostname' is null and listeningAddress is a wildcard address!");
}

GeneralName[] subjectAlternativeNamesArray = new GeneralName[subjectAlternativeNames.size()];
certGen.addExtension(Extension.subjectAlternativeName, subjectAlternativeNamesIsCritical, new GeneralNames(subjectAlternativeNames.toArray(subjectAlternativeNamesArray)));

ContentSigner sigGen;
try {
Expand Down

0 comments on commit ade79e4

Please sign in to comment.