java 如何使用 apache.commons.net.ftps 配置客户端身份验证?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13471015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 12:53:44  来源:igfitidea点击:

How to configure client authentication with apache.commons.net.ftps?

javasslapache-commons-netftpsauthentication

提问by Andy

I implemented a FTPS client (FTP over SSL/TLS) in java using the apache.commons.net-framework. It is configured to do explicit security on the default port 21.

我使用apache.commons.net框架在 java 中实现了一个 FTPS 客户端(FTP over SSL/TLS)。它被配置为在默认端口 21 上执行显式安全。

ftpsClient = new FTPSClient(false);
ftpsClient.setTrustManager(getConfiguration().getCertificatesManager());
ftpsClient.connect(getConfiguration().getHostName(), getConfiguration().getPort());

As long as I don't enforce client-authentication on the server, everything works fine. But I need to enable client-authentication, so I enforce it on the server and configured the client-system properties:

只要我不在服务器上强制执行客户端身份验证,一切正常。但是我需要启用客户端身份验证,因此我在服务器上强制执行并配置了客户端系统属性:

-Djavax.net.ssl.keyStore="D:/.../ftps-client-auth.keystore"
-Djavax.net.ssl.keyStorePassword="*****"
-Djavax.net.ssl.keyStoreType=JKS

What I got was the same as if I did not set the system properties:

我得到的和我没有设置系统属性一样:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1806)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:986)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1170)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1197)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1181)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:265)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:207)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:172)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:192)

The server-log says:

服务器日志说:

DEBUG: Client "<my ip address>", "SSL_accept failed: error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate"

Seems right - I enabled -Djavax.net.debug=all, what shows that the server sends a list of CNs it accepts, but the client sends an empty certificate chain.

看起来是对的 - 我启用了-Djavax.net.debug=all,这表明服务器发送了它接受的 CN 列表,但客户端发送了一个空的证书链。

  • What have I done wrong?
  • Do I need to do some configuration programatically?
  • Do the certificates or the private key need to support anything special for SSL/TLS?
  • 我做错了什么?
  • 我需要以编程方式进行一些配置吗?
  • 证书或私钥是否需要支持 SSL/TLS 的特殊功能?

回答by Andy

Figured it out: you need to programmatically set a KeyManager. Setting the system properties (-Djavax.net.ssl.keyStore, ...) is not sufficient, because the the framework does not use Suns SSLSocketFactory.

想通了:您需要以编程方式设置一个KeyManager. 设置系统属性 ( -Djavax.net.ssl.keyStore, ...) 是不够的,因为框架不使用 Suns SSLSocketFactory

Example:

例子:

ftpsClient = new FTPSClient(false);
ftpsClient.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
KeyManager keyManager = org.apache.commons.net.util.KeyManagerUtils.createClientKeyManager(new File(keystorePath), keystorePass);
ftpsClient.setKeyManager(keyManager);
ftpsClient.connect(getConfiguration().getHostName(), getConfiguration().getPort());

You may want to choose a different Trust-Manager, e.g. one that is based on a Java-keystore. The utils provide a method for that, too: TrustManagerUtils.getDefaultTrustManager(keystore)

您可能希望选择不同的 Trust-Manager,例如基于 Java 密钥库的信任管理器。utils 也为此提供了一种方法:TrustManagerUtils.getDefaultTrustManager(keystore)