禁用单个连接的SSLHandshakeException

maa*_*nus 6 java security ssl https

我正在寻找类似于这个答案的解决方案,但更安全.我想禁用证书验证,但仅针对单个请求(这是我所需要的).所以它应该做以下一个或多个

  • 一个请求完成后返回安全状态
  • 仅禁用给定URL的验证
  • (也许)仅针对一个线程使用不安全的设置

附录

当我要求更安全的解决方案时,我真的想知道这个问题(得分-2)与原始问题(得分+46)相比有什么问题.有人能解释一下吗

解释为什么我需要这个:有一个有效的服务器证书,通常,它被使用.但我需要发送一个请求localhost,它也必须在开发人员计算机上工作.必须是https因为没有http支持.

ped*_*ofb 7

解决方案

此实用程序类基于提供的示例.使用"宽松"SSLContext,预先配置TrustManager接受所有证书.如果您的localhost ssl证书已颁发给其他CN,则还需要主机名验证程序

将它应用于需要使用HttpsURLConnection.setSocketFactory和进行"放松"的ssl验证的每个连接HttpsURLConnection.setHostnameVerifier.默认行为不会更改

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class RelaxedSSLContext {
    // Create a trust manager that does not validate certificate chains like 
    public static TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() 
                return null;
            }
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                //No need to implement. 
            }
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                //No need to implement. 
            }
        }
    };

    //hostname verifier. All hosts valid
    public static HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };  

    //hostname verifier. Only localhost and 127.0.0.1 valid
    public static HostnameVerifier localhostValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return "localhost".equals(hostname) || "127.0.0.1".equals(hostname);
        }
    };  


    public static SSLContext getInstance() throws KeyManagementException, NoSuchAlgorithmException{
        return getInstance("SSL");
    }   

    //get a 'Relaxed'  SSLContext with no trust store (all certificates are valids) 
    public static SSLContext getInstance(String protocol) throws KeyManagementException, NoSuchAlgorithmException{
        SSLContext sc = SSLContext.getInstance(protocol);
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        return sc;
    }   
}
Run Code Online (Sandbox Code Playgroud)

以这种方式使用它

HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(RelaxedSSLContext.getInstance().getSocketFactory());
conn.setHostnameVerifier(RelaxedSSLContext.localhostValid);
Run Code Online (Sandbox Code Playgroud)

例1(默认)

url = "https://www.google.cop/finance";
conn = url.openConnection();
conn.connect();
int statusCode = conn.getResponseCode();   // 200
Run Code Online (Sandbox Code Playgroud)

示例2(错误的主机名)

url = "https://216.58.210.164/finance";
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(RelaxedSSLContext.allHostsValid); 
conn.connect();
int statusCode = conn.getResponseCode(); //200
// ERROR 'No subject alternative names matching IP address 216.58.210.164 found' without hostnameVerifier
Run Code Online (Sandbox Code Playgroud)

Example3(链不在默认信任库中)

url = "https://www.aragon.es";
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(RelaxedSSLContext.getInstance().getSocketFactory());
conn.connect();
int statusCode = conn.getResponseCode(); //200
// ERROR 'unable to find valid certification path to requested target' without relaxedSocketFactory
Run Code Online (Sandbox Code Playgroud)

上下文

我认为你的问题是关于主题的,有用的并且表达得很好.在某些情况下,应用高安全性的上下文是合适的,或者更确切地说,并不是必需的.普通HTTP仍在使用中......

让我们分析一下你的背景.必需的是通过HTTPS访问localhost而不信任服务器标识.这意味着您要接受服务器提供的任何证书.此场景的安全问题是MITM附加(中间的人).来自(维基百科)

攻击者秘密传递并可能改变双方之间的沟通,他们认为他们之间正在直接沟通.

但是有可能是MITM攻击与localhost的不受信任的https连接

请参阅https://security.stackexchange.com/questions/8145/does-https-prevent-man-in-the-middle-attacks-by-proxy-server/8309#8309

首先,在https中,服务器必须向客户端提供服务器证书.客户端验证证书的公钥并检查其是否与本地信任库匹配.通过网络管理员的协作,可以嗅探网络并设置代理来拦截连接.但恶意代理并不拥有匹配的私钥,因此代理服务器可能会尝试伪造证书并提供自己的公钥.证书不会出现在客户端信任中,因此连接将被拒绝.但是,如果删除信任库验证,理论上攻击是可能的.

但是,MITM是否可能限制与localhost的连接?

请参阅https://security.stackexchange.com/questions/61399/are-mitm-attacks-possible-on-http-loopbacks

在一般情况下,这是不可能的,但是具有对计算机的root访问权限的攻击者可以更改DNS并将请求重定向到恶意代理.即使使用127.0.0.1,如果应用程序有办法配置连接端口也是可能的.

偏执的解决方案可能是硬编码服务器连接URL,端口甚至可信任.但是你在开发环境中谈论localhost,所以我认为我们可以放松一下


Rom*_*man 3

只需使用实例方法setX()而不是 static setDefaultX()

HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(...);
connection.setHostnameVerifier(...);
Run Code Online (Sandbox Code Playgroud)