diff --git a/src/org/parosproxy/paros/network/SSLConnector.java b/src/org/parosproxy/paros/network/SSLConnector.java index c52c8f3e50d..a919b05cba7 100644 --- a/src/org/parosproxy/paros/network/SSLConnector.java +++ b/src/org/parosproxy/paros/network/SSLConnector.java @@ -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; @@ -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); @@ -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 @@ -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(); @@ -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); } @@ -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 @@ -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 diff --git a/src/org/parosproxy/paros/security/CachedSslCertifificateServiceImpl.java b/src/org/parosproxy/paros/security/CachedSslCertifificateServiceImpl.java index 0d1caf94763..5c1e00c44a1 100644 --- a/src/org/parosproxy/paros/security/CachedSslCertifificateServiceImpl.java +++ b/src/org/parosproxy/paros/security/CachedSslCertifificateServiceImpl.java @@ -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; @@ -51,18 +52,24 @@ private CachedSslCertifificateServiceImpl() { private Map 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} */ diff --git a/src/org/parosproxy/paros/security/SslCertificateService.java b/src/org/parosproxy/paros/security/SslCertificateService.java index 70e3a8f7cd2..4a39b592b6b 100644 --- a/src/org/parosproxy/paros/security/SslCertificateService.java +++ b/src/org/parosproxy/paros/security/SslCertificateService.java @@ -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; @@ -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 @@ -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; diff --git a/src/org/parosproxy/paros/security/SslCertificateServiceImpl.java b/src/org/parosproxy/paros/security/SslCertificateServiceImpl.java index 666285d4f17..e45909f0c7a 100644 --- a/src/org/parosproxy/paros/security/SslCertificateServiceImpl.java +++ b/src/org/parosproxy/paros/security/SslCertificateServiceImpl.java @@ -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; @@ -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; @@ -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. * @@ -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."); @@ -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"); @@ -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 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 {