我正在添加到我们的大型Java应用程序的模块必须与另一家公司的SSL安全网站进行交谈.问题是该站点使用自签名证书.我有一份证书副本,以验证我没有遇到中间人攻击,我需要将此证书合并到我们的代码中,以便与服务器的连接成功.
这是基本代码:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Run Code Online (Sandbox Code Playgroud)
在没有为自签名证书进行任何额外处理的情况下,这会在conn.getOutputStream()处死,但有以下异常:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path …Run Code Online (Sandbox Code Playgroud) 我正在使用Java 6,并尝试HttpsURLConnection使用客户端证书创建针对远程服务器.
服务器使用自签名根证书,并要求提供受密码保护的客户端证书.我已将服务器根证书和客户端证书添加到我在/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts(OSX 10.5)中找到的默认java密钥库中.密钥库文件的名称似乎表明客户端证书不应该进入那里?
无论如何,将根证书添加到这个商店解决了臭名昭着的问题 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.
但是,我现在仍然坚持如何使用客户端证书.我尝试了两种方法,并没有让我到任何地方.
首先,首选,尝试:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://somehost.dk:3049");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
// The last line fails, and gives:
// javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Run Code Online (Sandbox Code Playgroud)
我试过跳过HttpsURLConnection类(不理想,因为我想与服务器谈论HTTP),而是这样做:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
InputStream inputstream = sslsocket.getInputStream();
// do anything with the inputstream results in:
// java.net.SocketTimeoutException: Read timed out
Run Code Online (Sandbox Code Playgroud)
我甚至不确定客户端证书是否存在问题.
通过查看java.security我的文件JRE,我发现默认使用的密钥库类型设置为JKS.这里有一个可以使用的密钥库类型列表.
是否有推荐的密钥库类型?不同密钥库类型的优缺点是什么?
通过Java编程语言执行scp传输的最佳方法是什么?我似乎可以通过JSSE,JSch或充气城堡java库来执行此操作.这些解决方案似乎都没有一个简单的答案.
我有两个应用程序在同一个java虚拟机中运行,并且都使用不同的密钥库和信任库.
一个可行的选择是使用单个密钥库并将所有其他密钥库导入共享密钥库(例如keytool -import),但如果我可以为在同一个jvm中运行的单独应用程序使用单独的密钥库,它将真正有助于我的要求.
我可以将keystore和truststore设置为jvm参数或系统属性,如下所示:
java -Djavax.net.ssl.keyStore=serverKeys
-Djavax.net.ssl.keyStorePassword=password
-Djavax.net.ssl.trustStore=serverTrust
-Djavax.net.ssl.trustStorePassword=password SSLApplication
Run Code Online (Sandbox Code Playgroud)
要么
System.setProperty("javax.net.ssl.keyStore","serverKeys")
Run Code Online (Sandbox Code Playgroud)
但是这种方法的问题在于它指定了要在JVM级别使用的密钥库/信任库,因此在同一JVM中运行的所有应用程序都获得相同的密钥库/信任库.
我还尝试创建自定义SSLContext并将其设置为默认值,但它还为在同一JVM中运行的所有应用程序设置上下文.
SSLContext context = SSLContext.getInstance("SSL");
context.init(kms, tms, null);
SSLContext.setDefault(context);
Run Code Online (Sandbox Code Playgroud)
我希望能够使用不同的密钥库/信任库而无需修改单个应用程序代码.
除了jre中的默认密钥库/证书之外,还可以动态注册多个密钥库的解决方案非常棒.
解决方案将以这种方式工作:
请让我知道您的想法或解决方案.提前致谢!
我已经设置了一个自签名证书来测试ssl java连接 - 但是,它拒绝找到java trustStore.我已经在/ Java/jre6/lib/security中保存了它的副本,除了编译类的文件夹(使用netbeans)以及/ java/jre6/bin之外,上述任何一个都不起作用,因为当我运行以下 - trustStore = null.
public class ShowTrustStore {
public static void main(String[] args) {
System.setProperty("javax.net.ssl.keyStore", "keystore.jks");
System.setProperty("javax.net.ssl.trustStrore", "cacerts.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
String trustStore = System.getProperty("javax.net.ssl.trustStore");
if (trustStore == null) {
System.out.println("javax.net.ssl.trustStore is not defined");
} else {
System.out.println("javax.net.ssl.trustStore = " + trustStore);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如何正确设置路径?
**********UPDATE************使用getFile()方法和一些其他调试数据:
package ssltest;
public class Main {
public static void main(String[] args) {
// System.setProperty("javax.net.ssl.keyStore", "/keystore.jks");
// System.setProperty("javax.net.ssl.trustStrore", "/java.home/cacerts.jks");
// System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
// System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
try {
Main.class.getResource("trustStore.jks").getFile(); …Run Code Online (Sandbox Code Playgroud) 我对文件cacerts和jssecacerts文件之间的区别感到非常困惑.
我知道默认情况下java会查找该jssecacerts文件,然后查找该cacerts文件.
但jssecacerts文件的重点是什么?
我的理解是,如果需要使用新的信任库,则cacerts应该制作一份副本,并将所有新的可信CA添加到该副本中.cacerts然后,-Djavax.net.ssl.trustStore系统属性应引用(使用新CA)的副本.这样,在该计算机上运行的其他Java应用程序将不会意外地信任非默认CA.
在处理不同的问题(与RMI相关)时,我使用"无限强度"策略文件升级了系统的"安全文件夹",现在我的应用程序以不同的方式失败.我得到一个很长的堆栈转储,其后面的位看起来是相关的:
Exception in thread "main" java.lang.ExceptionInInitializerError
[...crop...]
Caused by: java.lang.SecurityException: Can not initialize cryptographic mechanism
at javax.crypto.JceSecurity.<clinit>(JceSecurity.java:86)
... 17 more
Caused by: java.lang.SecurityException: The jurisdiction policy files are not signed by a trusted signer!
[...crop...]
Run Code Online (Sandbox Code Playgroud)
嗯,WTF?唯一的变化是我将orignal jar文件放在一边,并在$ JAVA_HOME/lib/security中添加了无限制的文件.该目录现在看起来像这样:
$ ls
blacklist javaws.policy trusted.libraries
cacerts local_policy.jar US_export_policy.jar
java.policy local_policy.jar.strong US_export_policy.jar.strong
java.security local_policy.jar.unlimited US_export_policy.jar.unlimited
Run Code Online (Sandbox Code Playgroud)
当然,.strong和.unlimited版本都在那里,所以我可以快速切换回来.
方向简短明了,似乎只能设想替换这两个文件(local_policy.jar和US_exportpolicy.jar).
还需要做什么?
请注意,java和策略文件的版本是迄今为止最年轻的版本:1.7.0_03和jce_policy-6.
PS 这里发现的类似标题的文章根本没有任何帮助.
我正在编写一个Java客户端(需要在桌面JRE和Android上工作),以获得通过TLS承载的专有协议(特定于我的公司).我试图找出用Java编写TLS客户端的最佳方法,特别是确保它正确地进行主机名验证.(编辑:通过它,我的意思是检查主机名是否与X.509证书匹配,以避免中间人攻击.)
JSSE是编写TLS客户端的明显API,但我从" 世界上最危险的代码 "论文(以及实验)中注意到,JSSE在使用SSLSocketFactory API时不验证主机名.(这是我必须使用的,因为我的协议不是HTTPS.)
因此,似乎在使用JSSE时,我必须自己进行主机名验证.而且,而不是从头开始编写代码(因为我几乎肯定会弄错),似乎我应该"借用"一些有效的现有代码.所以,我发现的最有可能的候选者是使用Apache HttpComponents库(具有讽刺意味,因为我实际上并不在做HTTP)并使用org.apache.http.conn.ssl.SSLSocketFactory类代替标准的javax .net.ssl.SSLSocketFactory类.
我的问题是:这是一个合理的行动方案吗?或者我完全误解了事情,走出了深层,实际上有一种更简单的方法来获取JSSE中的主机名验证,而不需要像HttpComponents那样引入第三方库?
我也看过BouncyCastle,它有一个非JSSE的TLS API,但它似乎更有限,因为它甚至不进行证书链验证,更不用说主机名验证,所以它看起来像一个非-起动机.
编辑:这个问题已经回答了Java 7,但我仍然很好奇Java 6和Android的"最佳实践"是什么.(特别是,我必须为我的应用程序支持Android.)
再次编辑:为了使我的"借用Apache HttpComponents"更具体,我创建了一个小型库,其中包含从Apache HttpComponents中提取的HostnameVerifier实现(最值得注意的是StrictHostnameVerifier和BrowserCompatHostnameVerifier).(我意识到我需要的只是验证器,我不需要像我原先想的那样使用Apache的SSLSocketFactory.)如果留给我自己的设备,这就是我将使用的解决方案.但首先,我有什么理由不这样做吗?(假设我的目标是以与https相同的方式进行主机名验证.我意识到它本身可以辩论,并且已经在加密列表的线程中进行了讨论,但是现在我坚持使用类似HTTPS的主机名验证,即使我没有使用HTTPS.)
假设我的解决方案没有"错误",我的问题是:有没有"更好"的方法来实现它,同时仍然可以在Java 6,Java 7和Android上保持可移植性?("更好"意味着更惯用,已经广泛使用,和/或需要更少的外部代码.)
我在一个运行2或3个应用程序的系统中安装了一个java.
所有应用程序都使用相同的运行时.
有没有办法为ca证书指定不同的密钥库,而不是java_home/jre/lib/security中的密钥库.也就是说,是否可以选择指定一个"额外"密钥库,该密钥库被加载并添加到从java_home/jre/lib/security/cacerts加载的证书中?
我想避免的是每次升级框中的jdk时都必须重新导入我们的本地ca.
java ×10
jsse ×10
ssl ×8
truststore ×3
keystore ×2
security ×2
bouncycastle ×1
cryptography ×1
jsch ×1
jvm ×1
scp ×1