我想通过SSL连接到我的服务器。因此,我使用以下命令在服务器上生成证书:
openssl genrsa -out server.pem 2048
openssl req -new -x509 -nodes -sha1 -days 3650 -key server.pem > server.cert
如果我信任客户端上所有使用TrustManager的证书,则连接有效:
X509TrustManager tm = new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(
X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(
X509Certificate[] certs, String authType) {
}
};
但我当然不想信任所有证书,而只是我的。我尝试了几个命令来导入证书,如:
keytool -import -alias ca -file server.cert -keystore cacerts
但我总是得到这个错误:
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
我需要做些什么才能让这个工作?有人可以解释一个不熟悉cryto字段的人所需的步骤吗?
编辑:根据Donal Fellows的建议,我尝试了使用自定义X509TrustManager的方法,它可以工作。但这样安全吗?如果我只是返回" null"在方法" getAcceptedIssuers"它的工作也很好,我不太清楚为什么:
X509TrustManager tm = new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
X509Certificate[] trustedCerts = new X509Certificate[1];
try{
InputStream inStream = new FileInputStream("server.cert");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
inStream.close();
trustedCerts[0] = cert;
}catch(Exception e){
e.printStackTrace();
}
return trustedCerts;
}
@Override
public void checkClientTrusted(
X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
boolean match = false;
try{
InputStream inStream = new FileInputStream("server.cert");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
inStream.close();
for(X509Certificate c : chain){
if(c.equals(cert)){
match = true;
}
}
}catch(Exception e){
throw new CertificateException();
}
if(!match)
throw new CertificateException();
}
};
答案 0 :(得分:0)
如果您确实将事情锁定,可以通过安装自定义X509TrustManager
进行测试,以查看所使用的证书与您认为的证书相等应该是(你肯定知道的;你已经生成了它)。这实际上非常安全,但允许没有操作灵活性;如果服务器受到攻击并且您必须重新生成密钥,则所有客户端也需要更新。
因为这非常令人讨厌(并且不会像WWW那样扩展),所以使用信任根更为正常。信任根是一种自签名CA证书,通常具有较长的使用寿命,用于签署部署到服务器的工作证书。 (您可以使用CA证书本身,但通常最好将其保持脱机状态,可能在防火保险箱中的可移动介质上。)然后将CA证书放入信任库(即只保存证书的密钥库) )在客户端上并告诉Java使用它。
在实践中,你几乎就在那里。您可能只需要告诉Java使用您的cacerts
信任库。您可以通过设置correct system property。
// Be careful on Windows; this property (unusually!) uses “/” instead of “\”
System.setProperty("javax.net.ssl.trustStore", "/path/to/cacerts");