Il mio problema:Come posso recuperare un certificato del server SSL non attendibile per poterlo verificare e fidarsi?
Voglio collegarmi ai server (non limitato al protocollo HTTPS - potrebbe essere LDAP-over-SSL, potrebbe essere SMTPS, potrebbe essere IMAPS, ecc.) Che potrebbe utilizzare i certificati che Java non si fideranno di default (perché sono autofirmati).
Il flusso di lavoro desiderato è tentare la connessione, recuperare le informazioni sul certificato, presentarlo all'utente e, se lo accetta, aggiungerlo al truststore in modo che possa essere considerato affidabile in futuro.
Sono bloccato al recupero del certificato. Ho il codice (vedi alla fine del post) che ho cribbed da qui e dai siti a cui si fa riferimento con le risposte alle domande su SSL Java. Il codice crea semplicemente un SSLSocket
, avvia l'handshake SSL e richiede la sessione SSL per lo Certificate[]
. Il codice funziona correttamente quando mi collego a un server utilizzando un certificato già affidabile. Ma quando mi collego a un server utilizzando un CERT auto-firmato ho la solita:
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
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
[etc]
Se corro con -Djavax.net.debug=all
vedo che la JVM fa recuperare il CERT auto-firmato, ma farà saltare la connessione per l'utilizzo un certificato non attendibile prima di arrivare al punto in cui restituirà i certificati.
Sembra un problema di pollo e uova. Non mi consente di vedere i certificati perché non sono attendibili. Ma ho bisogno di vedere i certificati per essere in grado di aggiungerli al truststore in modo che siano attendibili. Come si esce da questo?
Per esempio, se faccio funzionare il programma come:
java SSLTest www.google.com 443
ricevo una stampa dei certs Google sta utilizzando. Ma se lo eseguo come
java SSLTest my.imap.server 993
Ricevo l'eccezione di cui sopra.
Il codice:
import java.io.InputStream;
import java.io.OutputStream;
import java.security.cert.*;
import javax.net.SocketFactory;
import javax.net.ssl.*;
public class SSLTest
{
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: SSLTest host port");
return;
}
String host = args[0];
int port = Integer.parseInt(args[1]);
SocketFactory factory = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.startHandshake();
Certificate[] certs = socket.getSession().getPeerCertificates();
System.out.println("Certs retrieved: " + certs.length);
for (Certificate cert : certs) {
System.out.println("Certificate is: " + cert);
if(cert instanceof X509Certificate) {
try {
((X509Certificate) cert).checkValidity();
System.out.println("Certificate is active for current date");
} catch(CertificateExpiredException cee) {
System.out.println("Certificate is expired");
}
}
}
}
}
Grazie! Solo il genere di cose che stavo cercando. E per la cronaca, ecco una versione solo per console della stessa cosa: http://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/ util/InstallCert.java – QuantumMechanic