Подтвердить сертификат X.509 против CA в Java
Предположим, что у меня есть что-то вроде этого (код на стороне клиента):
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, trustAllCerts, null);
SocketFactory sf = sslc.getSocketFactory();
SSLSocket s = (SSLSocket) sf.createSocket("127.0.0.1", 9124);
Этот код является полным функциональным, но я действительно не могу понять, как проверить сертификат сервера на один конкретный сертификат ЦС, который у меня есть в файле pem.
Все сертификаты подписаны моим самозаверяющим ЦС, и это CA, которому я должен проверять (только против этого).
Каждый ответ оценивается.
EDIT:
В ответ на jglouie (спасибо вам большое - не можете проголосовать за свой ответ).
Я основал решение:
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws CertificateException {
InputStream inStream = null;
try {
// Loading the CA cert
URL u = getClass().getResource("tcp/cacert.pem");
inStream = new FileInputStream(u.getFile());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
for (X509Certificate cert : certs) {
// Verifing by public key
cert.verify(ca.getPublicKey());
}
} catch (Exception ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
inStream.close();
} catch (IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
};
Ответы
Ответ 1
Я предполагаю, что самозаверяющий сертификат вашего ЦС уже загружен следующим образом:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream finStream = new FileInputStream("CACertificate.pem");
X509Certificate caCertificate = (X509Certificate)cf.generateCertificate(finStream);
Затем в методе проверки сертификата:
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws CertificateException {
if (certs == null || certs.length == 0) {
throw new IllegalArgumentException("null or zero-length certificate chain");
}
if (authType == null || authType.length() == 0) {
throw new IllegalArgumentException("null or zero-length authentication type");
}
//Check if certificate send is your CA's
if(!certs[0].equals(caCertificate)){
try
{ //Not your CA's. Check if it has been signed by your CA
certs[0].verify(caCertificate.getPublicKey())
}
catch(Exception e){
throw new CertificateException("Certificate not trusted",e);
}
}
//If we end here certificate is trusted. Check if it has expired.
try{
certs[0].checkValidity();
}
catch(Exception e){
throw new CertificateException("Certificate not trusted. It has expired",e);
}
}
Отказ от ответственности: Даже не пытались скомпилировать код
Ответ 2
Принятый ответ крайне неверен. Он не криптографически не проверяет соединение между сертификатом сервера и доверенным центром сертификации. В общем, вам почти не нужно будет внедрять собственный TrustManager, поэтому очень опасно.
Как указано в EJP, нет необходимости реализовывать собственный TrustManager, вы можете просто использовать стандартную версию и убедиться, что доверенный сертификат CA был добавлен в ваш TrustStore по умолчанию. См. этот вопрос для получения дополнительной информации.
Взгляните на класс CertPathValidator из JDK, который проверяет непрерывную цепочку доверия от собственного сертификата сервера до доверенный ЦС. См. Oracle docs для ознакомления с проверкой цепочки сертификатов.
Ответ 3
Этот код полностью функциональный
Этот код полностью дисфункциональный. Он абсолютно небезопасен, а также не соответствует его собственным спецификациям. Существует редко необходимость поставлять собственный TrustManager, по умолчанию он работает очень хорошо.
Все, что вам нужно сделать, это убедиться, что сертификат ЦС, который у вас есть, присутствует в вашем доверенном магазине, а затем установить системное свойство javax.net.ssl.trustStore
, чтобы указать на него, если он не является файлом доверия доверия по умолчанию. Вам не нужно писать код вообще, кроме, возможно, System.setProperty()
, если вы не установите его с помощью командной строки -D.
РЕДАКТИРОВАТЬ Ваше "решение", конечно, не будет работать вообще. Он предполагает, что каждый сертификат в цепочке подписывается вашим сертификатом. Это может быть справедливо только для цепей длиной 1 или длины 2, если сертификат подписи = ваш сертификат.