我没有丝毫的想法,我应该如何查找或使用它,我所要做的只是C#示例代码,这与我发现的所有Java答案有很大的不同. (例如,KeyStore是否需要某种密码?)
这是我拥有的C#示例代码
System.Security.Cryptography.X509Certificates.X509CertificateCollection SSC_Certs = new System.Security.Cryptography.X509Certificates.X509CertificateCollection(); Microsoft.Web.Services2.Security.X509.X509CertificateStore WS2_store = Microsoft.Web.Services2.Security.X509.X509CertificateStore.CurrentUserStore( Microsoft.Web.Services2.Security.X509.X509CertificateStore.MyStore); WS2_store.OpenRead(); Microsoft.Web.Services2.Security.X509.X509CertificateCollection WS2_store_Certs = WS2_store.Certificates;
然后它只是遍历WS2_store_Certs CertificateCollection,并以这种方式进行检查.
再进一步,它设置了这样的证书:
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url_string); httpWebRequest.ClientCertificates = SSC_Certs;
这一切看起来相当合乎逻辑,即使我不知道它如何找到证书,但是我仍然找不到Java的等同物.
UPDATE
我正在制作的连接是依赖于JDK 5的较大应用程序的一部分,但是我已经设法使用sunmscapi jar找到我正在寻找的证书.当我尝试使用Windows密钥库连接时出现错误,所以我以为我从Windows存储中获取我需要的证书并将其插入到默认java中,因此我想到了这个问题.现在我得到一个EOFException,然后是SSLHandshakeException,说“远程主机在握手期间关闭连接”. ssl调试跟踪并没有显示立即的问题,因为我需要的证书显示在证书链中.
它完成了整个ClientKeyExchange的事情,说完了,然后我从调试日志中得到的最后一个消息就是
[write] MD5 and SHA1 hashes: len = 16 0000: 14 00 00 0C D3 E1 E7 3D C2 37 2F 41 F9 38 26 CC .......=.7/A.8&. Padded plaintext before ENCRYPTION: len = 32 0000: 14 00 00 0C D3 E1 E7 3D C2 37 2F 41 F9 38 26 CC .......=.7/A.8&. 0010: CB 10 05 A1 3D C3 13 1C EC 39 ED 93 79 9E 4D B0 ....=....9..y.M. AWT-EventQueue-1,WRITE: TLSv1 Handshake,length = 32 [Raw write]: length = 37 0000: 16 03 01 00 20 06 B1 D8 8F 9B 70 92 F4 AD 0D 91 .... .....p..... 0010: 25 9C 7D 3E 65 C1 8C A7 F7 DA 09 C0 84 FF F4 4A %..>e..........J 0020: CE FD 4D 65 8D ..Me. AWT-EventQueue-1,received EOFException: error
而我正在使用的代码来建立连接
KeyStore jks = KeyStore.getInstance(KeyStore.getDefaultType()); jks.load(null,null); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); jks.setCertificateEntry("alias",cert1); //X509Certificate obtained from windows keystore kmf.init(jks,new char[0]); SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(kmf.getKeyManagers(),new TrustManager[]{tm},null); sslsocketfactory = sslContext.getSocketFactory(); System.setProperty("https.proxyHost",proxyurl); System.setProperty("https.proxyPort",proxyport); Authenticator.setDefault(new MyAuthenticator("proxyID","proxyPassword")); URL url = new URL(null,urlStr,new sun.net.www.protocol.https.Handler()); HttpsURLConnection uc = (HttpsURLConnection) url.openConnection(); uc.setSSLSocketFactory(sslsocketfactory); uc.setAllowUserInteraction(true); uc.setRequestMethod("POST"); uc.connect();
(我还没有尝试HttpClient,因为我不知道如何找到证书文件,我也不知道这在每个客户端系统上是否一样).
另一个更新
我在WINDOWS-ROOT密钥库中找到了我需要的证书的CA(并使用.verify()检查它们是否已经检出),我也将它们添加到了java密钥库中,但仍然没有任何变化.我想他们应该进入TrustStore,但是我还没有找到一种方法来编程. (不想依靠最终用户做这样的事情,因为我可以保证的是证书和CA将由于在这个可笑的漫长问题开始时提到的第三方软件而存在).
更多更新
加上以前的更新,我得出结论,我的问题必须在于我的CA不在Java的cacerts文件中,所以它从服务器获取可信CA的列表,但不能识别它们随后不会发送单个证书,导致连接失败.
所以问题依然存在,如何让Java将密钥库用作信任库或以编程方式添加证书到Cacerts(而不需要文件路径)?因为如果那些不可能只是让我秘密选择C,巫毒.我会用针头刺一个公爵娃娃,以防万一.
解决方法
>你必须有一个密钥库,其中有你的证书文件. (我知道你的情况并不完全允许这一点,但是请稍等一下,以便我们可以把你的问题缩小一点,因为有一点太复杂,无法直接回答.)
所以,先把你的手放在实际的证书文件上.如果您在Windows 7上,可以通过以下步骤完成:
>打开Internet Explorer,
>打开工具(在Internet Explorer 9中它是cog图标),
>单击Internet选项,
>转到内容标签,
>单击证书,
找到手头的证书,
>单击它并单击导出并将其保存到文件(DER编码的二进制X.509).
(导出后,将其从其他证书中删除,确保Java不会以任何方式使用它,我不知道是否可以使用它,但不会受到伤害)
现在,您必须创建一个密钥存储文件,并将导出的证书导入到其中,这可以通过以下方式完成.
> keytool -importcert -file <certificate> -keystore <keystore> -alias <alias>
(显然,keytool必须在你的工作路上,它是JDK的一部分.)
它将提示您输入密码(密钥存储区的密码,不必对证书进行任何操作),我现在不知道如何设置为“”,所以设置为密码或其他.
之后,通过以下步骤,您可以通过代理建立到您的端点的安全连接.
>首先,加载密钥存储文件.
InputStream trustStream = new FileInputStream("path/to/<keystore>"); char[] trustPassword = "<password>".tocharArray();
>初始化KeyStore.
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(trustStream,trustPassword);
>初始化TrustManager对象. (我认为这些处理证书的解决方案或类似的东西,但是,就我而言,这是魔术.)
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustFactory.init(trustStore); TrustManager[] trustManagers = trustFactory.getTrustManagers();
>创建一个新的SSLContext,将TrustManager对象加载到它,并将其设置为默认值.请注意,因为SSLContext.getDefault()返回一个不可修改的类的实例(或者更像是默认的实例),这就是为什么我们必须使用SSLContext.getInstance(“SSL” ).此外,不要忘记将这个新的SSLContext设置为默认值,因为没有这个代码就会变成poof.
SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null,trustManagers,null); SSLContext.setDefault(sslContext);
>为其创建代理和设置身份验证. (而不是使用System.setProperty(…)使用Proxy类哦,不要被Type.HTTP误导)
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress("<host>",<port>));
>为您的代理设置身份验证. (我已经使用了a free proxy,这不需要身份验证,所以我现在无法测试这部分问题.)
Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("<user>","<password>".tocharArray()); } });
通过将以前创建的代理传递给连接来连接到您的终点. (我已经使用了我公司的一个服务的URL,该网址要求一个证书 – 当然我在自己的密钥存储区输入了证书.)
URL url = new URL("<endpoint>"); URLConnection connection = url.openConnection(proxy); connection.connect();
如果它不这样工作(你得到错误),然后尝试与HttpsURLConnection.
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; httpsConnection.setAllowUserInteraction(true); httpsConnection.setRequestMethod("POST");
基本上,如果服务器(连接到的URL指向的资源所在的位置)要求提供凭据,那么setAllowUserInteraction将会启动,对吧?现在,我本来不能测试,但是看到如果你能让这个宝宝使用不需要身份验证的服务器来访问它的资源,那么你很好,因为服务器会问您只有在建立连接后才能验证自己.
如果在这些之后你仍然收到一些错误,那么请张贴.