我的主机名错误异常。我在我的程序中使用过this code(从某个链接获取)。我的程序工作正常。我的问题是否足够安全? (因为它没有验证证书链)
public class Host {
public String subscribe() throws Exception {
String resp = "";
String urlString="https://xxx.xxx.xx.xx:8443/WebApplication3/NewServlet";
URL url;
URLConnection urlConn;
DataOutputStream printout;
DataInputStream input;
String str = "";
int flag=1;
try {
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
System.out.println("Warning: URL Host: " + urlHostName + " vs. "
+ session.getPeerHost());
return true;
}
};
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);
url = new URL(urlString);
urlConn = url.openConnection();
urlConn.setDoInput(true);
Object object;
urlConn.setUseCaches(false);
urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
input = new DataInputStream(urlConn.getInputStream());
while (null != ((str = input.readLine()))) {
if (str.length() >0) {
str = str.trim();
if(!str.equals("")) {
//System.out.println(str);
resp += str;
}
}
}
input.close();
} catch ( MalformedURLException mue) {
mue.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
}
return resp;
}
public static class miTM implements javax.net.ssl.TrustManager,
javax.net.ssl.X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
return;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
return;
}
}
private static void trustAllHttpsCertificates() throws Exception {
// Create a trust manager that does not validate certificate chains:
javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
javax.net.ssl.TrustManager tm = new miTM();
trustAllCerts[0] = tm;
javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
}
答案 0 :(得分:4)
miTM
中的代码实际上禁用了任何SSL安全检查,因此安全级别非常低(如果SSL证书被破坏,您只会遇到错误,但是当证书没有时,您不会收到错误匹配域名。)
基本上,您尝试在没有任何安全性的情况下建立连接。如果这就是你想要的,解决方案可能“足够安全”,但最有可能的答案是“不”。
此类问题的正确解决方案是为此域创建匹配的证书。
不幸的是,当您的HTTP服务器使用“虚拟主机”(=许多域名映射到同一IP地址)时,这是不可能的。解决此问题的正确方法是获取您自己的IP地址。
如果您仍想尝试仅使用Java的解决方案,请查看以下答案:https://stackoverflow.com/a/3293720/34088
答案 1 :(得分:1)
这是一种清理代码并保持安全的方法。我想代码连接到已知服务(可信)。要使Java SSL堆栈接受连接,即使主机名不匹配,最好的方法是将服务器证书添加到JVM信任库。
首先,您可以从浏览器导出服务器证书并将其保存在磁盘上。在Linux中,您可以使用openssl s_client -connect xxx.xxx.xx.xx:8443
并将ascii-armored格式的服务器证书复制/粘贴到文本文件中。
然后使用keytool
将服务器证书导入jre/lib/security/cacerts
JKS文件
keytool -import -alias myservice -file servercertificate.cer
我更喜欢的另一个选项,就是在Java更新时避免回归,就是在你自己的地方复制cacerts
并通过javax.net.ssl.trustStore
系统属性声明它。
由于服务器证书位于信任存储区中...它是可信的,直到它过期。这通常用于自签名服务器证书。
答案 2 :(得分:1)
在java中曾经多次出现过这种异常
问题可能是ipconflict / ip-domain mismatch / invalid certificate
我通过使用适当的IP地址和安装证书解决了这个问题。
答案 3 :(得分:1)
为确保连接安全,您必须(至少):
您的代码在这两点上失败了:
TrustManager
根本不检查证书(它从不抛出异常,而API期望在证书不受信任时抛出CertificateException
形式) true
。修复您的代码:
TrustManagerFactory
初始化它们。您的问题标题(“主机名错误”)和您的示例网址https://xxx.xxx.xx.xx:8443
似乎建议您连接到IP地址。
与某些浏览器不同,Java严格遵循规范(RFC 2818):
如果存在类型为dNSName的subjectAltName扩展名,则必须将其用作标识。否则,(最具体)共同 必须使用证书的“主题”字段中的名称字段。 虽然使用通用名称是现有的做法,但确实如此 不推荐使用,并鼓励认证机构使用 而是dNSName。
[...]
在某些情况下,URI被指定为IP地址而不是主机名。在这种情况下,必须存在iPAddress subjectAltName 在证书中,必须与URI中的IP完全匹配。
这意味着您不能只是将IP地址放在服务器证书中的主题DN的公用名(CN)中。如果您使用的是IP地址,则必须在主题备用名称条目中。 (从Java 7开始,keytool
具有生成此类证书的选项。)
您可以在this answer中找到有关使用哪些命令的更多详细信息。
话虽如此,使用IP地址最多只能在测试环境中工作。我认为任何商业CA都不会为您提供基于IP地址的证书。我建议设置DNS条目(即使它只是在测试环境中的hosts
文件中)。
即使您没有使用IP地址,也必须确保该证书对您尝试与服务器联系的主机名有效:如果您有主题备用名称条目,其中一个必须匹配主机名;否则,主机名必须位于此证书的主题DN的CN RDN中。