2015-09-10 27 views
10

Sto testando la comunicazione SSL tra client e server localmente. Così ho generato il certificato usando i comandi OpenSSL. Aggiunto questo certificato nel file cacert. Anche generato file .p12.Restlet javax.net.ssl.SSLHandshakeException: null cert chain

Sto utilizzando lo stesso file .p12 in server e client. Questo è il codice del server

Server server = component.getServers().add(Protocol.HTTPS, port); 
Series<Parameter> params = server.getContext().getParameters(); 

params.add("keystorePath", ".p12 file path"); 
params.add("keystoreType", "PKCS12"); 
params.add("needClientAuthentication","true"); 

component.getDefaultHost().attach("", "/AA"), new AAClass()); 
component.start(); 

E questo è il codice cliente:

Client client = trustAllCerts(); 
clientResource = new ClientResource(url); 
clientResource.setNext(client); 
try{ 
     clientText = clientResource.post""); 
} 
catch(ResourceException e){ 
    e.printStackTrace(); 
} 

public Client trustAllCerts() { 
    Client client = null; 
    try { 
     client = new Client(new Context(), Protocol.HTTPS); 
     Context context = client.getContext(); 


     final SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); 
     context.getAttributes().put("sslContextFactory", new SslContextFactory() { 
      public void init(Series<Parameter> parameters) { 

      } 

      public SSLContext createSslContext() { 
       return sslContext; 
      } 
     }); 
     TrustManager tm = new X509TrustManager() { 
      public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
      } 

      public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
      } 

      public X509Certificate[] getAcceptedIssuers() { 
       return null; 
      } 
     }; 
     context.getAttributes().put("hostnameVerifier", new HostnameVerifier() {     
      @Override 
      public boolean verify(String arg0, SSLSession arg1) { 
       return true; 
      } 

     });   

     sslContext.init(null, new TrustManager[] { tm }, null);   

    } catch (KeyManagementException e) { 
     LOGGER.error("Exception in Key Management" + e); 
    } catch (NoSuchAlgorithmException e) { 
     LOGGER.error("Exception in Algorithm Used" + e); 
    } 
    return client; 
} 

sto ottenendo seguente eccezione:

Restlet-1299242, fatal error: 42: null cert chain 
javax.net.ssl.SSLHandshakeException: null cert chain 
%% Invalidated: [Session-25, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256] 
Restlet-1299242, SEND TLSv1.2 ALERT: fatal, description = bad_certificate 
Restlet-1299242, WRITE: TLSv1.2 Alert, length = 2 
Restlet-1299242, fatal: engine already closed. Rethrowing javax.net.ssl.SSLHandshakeException: null cert chain 
Restlet-1299242, called closeInbound() 
Restlet-1299242, fatal: engine already closed. Rethrowing javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack? 
Restlet-1299242, called closeOutbound() 
Restlet-1299242, closeOutboundInternal() 

Ho provato ad aggiungere keystore e truststore utilizzando System.setProperty() ma non ha funzionato.

Per favore aiuto. Grazie in anticipo.

+0

Come si crea il certificato autofirmato? Potresti postare i tuoi passi? E quando ottieni questa eccezione (mentre il server sta iniziando, mentre il client sta iniziando, quando il client ha fatto una richiesta al server, ecc)? Hai provato a connetterti con altri client di riposo come i browser? Se il certificato è attivo e pronto sul tuo server e se hai fatto una richiesta GET tramite il browser, devi ricevere un avviso sul tuo fornitore di certificati. – bhdrkn

+0

In realtà ho scaricato il certificato da un fornitore di certificati (di cui sopra l'esempio è relativo alla localizzazione del problema), nel pacchetto di download ottengo un certificato dell'autorità di root, un certificato di autorità intermedio, un certificato e una chiave. Ho provato tutte le combinazioni, ma ho ancora ottenuto l'eccezione della catena di cert null. Inoltre sto ottenendo un'eccezione quando il client è in grado di comunicare con il server. Inoltre nel browser ho visto un avviso sul certificato non attendibile, quindi ho aggiunto il certificato nel browser, ma continuo ad avere problemi con la catena del cert nulla. – vikas27

risposta

7

Innanzitutto, consente di creare un archivio di chiavi formattato JKS. PKCS12 viene solitamente utilizzato nel browser e sulle applicazioni java predefinite viene utilizzato JKS (per quanto ne so). Java supporta anche PKCS12 ma non conosco i parametri esatti per questo.

Preparazione JKS File

consente di guardare nel nostro file PKCS12 e ottenere gli alias di certificazione che vogliamo estrarre il nostro file JKS.

keytool -list \ 
     -keystore [*.p12 file] \ 
     -storepass [password] \ 
     -storetype PKCS12 \ 
     -v 

Nota gli alias che si desidera esportare. E ora creiamo un file JKS.

keytool -keystore [*.jks file path] -genkey -alias client 

Questo farà molte domande. Puoi riempirli come preferisci. Ora puoi esportare i tuoi alias dal file * .p12 al file * .jks.

keytool -importkeystore \ 
     -srckeystore [*.p12 file path] \ 
     -srcstoretype pkcs12 \ 
     -srcalias [alias from first command] \ 
     -destkeystore [*.jks file path] \ 
     -deststoretype jks \ 
     -deststorepass [*.jks file password] \ 
     -destalias [new alias] 

Se non si dispone di alcun file PKCS12, o i certificati sono in CER, DER o PEM formato è possibile aggiungere i certificati al vostro chiavi utilizzando il comando seguente.

keytool -import \ 
     -alias [new alias] \ 
     -keystore [*.jks file path] \ 
     -file [*.DER file path] 

E assicurarsi di aver importato, il certificato, il certificato del fornitore di certificato (certificato intermedio) e certificato di origine.

Ora è possibile verificare che il file JKS contenga tutti i certificati necessari.

keytool -list \ 
     -keystore [*.jks file path] \ 
     -storepass [password] \ 
     -storetype jks \ 
     -v 

Impostazione Server

È possibile utilizzare il file JKS sia sul client e lato server. Secondo Restlet documentation è possibile utilizzare il file JKS come questo per fornire la connessione HTTPS.

Server server = component.getServers().add(Protocol.HTTPS, port); 
Series<Parameter> parameters = server.getContext().getParameters(); 
parameters.add("sslContextFactory","org.restlet.engine.ssl.DefaultSslContextFactory"); 
parameters.add("keyStorePath", "*.jks file"); 
parameters.add("keyStorePassword", "password"); 
parameters.add("keyPassword", "password"); 
parameters.add("keyStoreType", "JKS"); 

Successivamente, se si controlla la porta dal browser è necessario vedere un segno sicuro. Oppure puoi utilizzare alcuni strumenti online (like this one) per verificare il tuo certificato.

Impostazione client

Vediamo ora lato client. Poiché stai sviluppando entrambi i lati dell'applicazione puoi utilizzare il file JKS già creato.

Context con = new Context(); 
Series<Parameter> clParameters = con.getParameters(); 
clParameters.add("truststorePath", "*.jks file"); 
clParameters.add("truststorePassword", "password"); 
clParameters.add("truststoreType", "JKS"); 
Client restletClient = new Client(con, Protocol.HTTPS); 

Durante il test o in altre circostanze, il proprio nome host certificato e il vostro nome host attuale potrebbero non corrispondere. Per disabilitare i controlli del nome host è possibile aggiungere questo blocco alla propria applicazione.

static{ 
    javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
    new javax.net.ssl.HostnameVerifier(){ 

     public boolean verify(String hostname, 
       javax.net.ssl.SSLSession sslSession) { 
      return true ; 
     } 
    }); 
} 

Alcuni pensieri

Poiché non posso testarlo sul mio locale, io non sono esattamente sicuro che il file JKS client e server deve essere lo stesso. Potrebbe essere necessario solo aggiungere il proprio certificato al server.jks. SSL e certificati sono sempre difficili per me. Di solito lo faccio funzionare dopo alcune prove ed errori. Spero che questo ti possa aiutare.

Inoltre, si può anche prendere in considerazione, utilizzando un tipo di server proxy inverso come Apache2 o Nginx. Se si desidera utilizzarli, è necessario unire i certificati in un singolo file. Se guardate il file del certificato si vede che ogni file (il proprio certificato, certificato intermedio e certificato di origine) è come questo

-----BEGIN CERTIFICATE----- 
MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUA... 
.... 
-----END CERTIFICATE----- 

È necessario aggiungere semplicemente uno di loro per creare un certificato fusa. E poi usa quel certificato per terminare SSL su Apache2 o Nginx. Questo è quello che faccio di solito. Ma dal lato client è ancora necessario creare file JKS.

+0

Ho provato quello che mi hai suggerito, ma ho comunque ottenuto la stessa eccezione. – vikas27

+0

@vikasTheJavaDeveloper Usi la tua versione del client con 'SSLContextFactory' e' Trustmanager' personalizzati o usa la mia versione? Utilizzi la tua vera catena di certificati o un certificato autofirmato? Potresti provare ad eseguire entrambe le applicazioni con il flag '-Djavax.net.debug = all', per vedere le richieste di certificati? E potresti pubblicare i log da qualche parte (forse gist) e aggiungere un link alla tua domanda? – bhdrkn

1

Un'opzione è leggere il file p12/pfx, ottenere i certificati e usarli per costruire KeyStores e TrustStores a livello di programmazione. Se l'input è un file pfx contenente un certificato di origine CA e un certificato client correlato, i metodi mostrati nella classe SslUtils di seguito consentiranno di farlo.
Tuttavia, c'è un avvertimento: il server Restlet predefinito (versione 2.3.4) non preleverà i certificati inviati dal client. Sono riuscito a risolvere questo problema (non è carino), vedere la mia risposta su this question.

mi concentrerò sulla configurazione delle connessioni sicure qui, ma tutto il codice sorgente e un esempio di lavoro è disponibile nel progetto restlet-clientcert Github. Il progetto Github è il risultato di me pensando di sapere cosa sto facendo, non avendo fortuna e nessuna esperienza con Restlet, ma mordendo comunque il proiettile così posso sentirmi un po 'meglio sapendo che potrei ottenere questo materiale di base per lavoro.

Sul lato server, utilizzare un numero personalizzato ServerSslContextFactory che configura in modo programmatico lo SSLContext utilizzato. Registrati fabbrica personalizzato con:

ServerSslContextFactory sslCtx = new ServerSslContextFactory(); 
sslCtx.init(certFileName, certFilePwd); 
ConcurrentMap<String, Object> attribs = server.getContext().getAttributes(); 
attribs.put("sslContextFactory", sslCtx); 

e allegare una "guardia" per estrarre le informazioni certificato client:

CertificateAuthenticator guard = new CertificateAuthenticator(server.getContext()); 
guard.setNext(MyRestlet.class); 
component.getDefaultHost().attachDefault(guard); 

Il ServerSslContextFactory:

public class ServerSslContextFactory extends DefaultSslContextFactory { 

    private static final Logger log = LoggerFactory.getLogger(ServerSslContextFactory.class); 

    protected DefaultSslContext wrappedCtx; 

    public void init(String certFileName, char[] certFilePwd) throws Exception { 

     if (log.isDebugEnabled()) { 
      log.debug("Loading certificates from [" + certFileName + "] and using " 
        + (certFilePwd != null && certFilePwd.length > 0 ? "a" : "no") + " password."); 
     } 
     Path certFilePath = Paths.get(Thread.currentThread().getContextClassLoader().getResource(certFileName).toURI()); 
     KeyManagerFactory kmf = SslUtils.loadKeyStore(certFilePath, certFilePwd); 
     KeyManager[] kms = kmf.getKeyManagers(); 
     List<X509Certificate> certs = SslUtils.getClientCaCerts(kms); 
     TrustManagerFactory tmf = SslUtils.createTrustStore(Constants.CERT_CA_ALIAS, certs.get(0)); 
     TrustManager[] tms = tmf.getTrustManagers(); 

     super.setNeedClientAuthentication(true); 

     SSLContext ctx = SSLContext.getInstance(SslUtils.DEFAULT_SSL_PROTOCOL); 
     ctx.init(kms, tms, null); 
     wrappedCtx = (DefaultSslContext) createWrapper(ctx); 
    } 

    @Override 
    public void init(Series<Parameter> parameters) { 
     log.debug("Not using parameters to initialize server SSL Context factory."); 
    } 

    @Override 
    public SSLContext createSslContext() throws Exception { 
     return wrappedCtx; 
    } 

    @Override 
    public boolean isNeedClientAuthentication() { 

     if (log.isDebugEnabled()) { 
      //log.debug("Needing client auth: " + super.isNeedClientAuthentication(), new RuntimeException("trace")); 
      log.debug("Needing client auth: " + super.isNeedClientAuthentication()); 
     } 
     return super.isNeedClientAuthentication(); 
    } 

} 

Sul lato client, una cosa simile:

ClientSslContextFactory sslCtx = new ClientSslContextFactory(); 
sslCtx.init(certFileName, certFilePwd); 
attribs.put("sslContextFactory", sslCtx); 

Impostare anche un hostnameVerifier (come mostrato nella domanda) per non verificare i nomi degli host.
Il ClientSslContextFactory:

public class ClientSslContextFactory extends SslContextFactory { 

    private static final Logger log = LoggerFactory.getLogger(ClientSslContextFactory.class); 

    protected KeyManager[] kms; 
    protected TrustManager[] tms; 

    public void init(String certFileName, char[] certFilePwd) throws Exception { 

     log.debug("Loading certificates from [" + certFileName + "] and using " 
       + (certFilePwd != null && certFilePwd.length > 0 ? "a" : "no") + " password."); 
     Path certFilePath = Paths.get(Thread.currentThread().getContextClassLoader().getResource(certFileName).toURI()); 
     KeyManagerFactory kmf = SslUtils.loadKeyStore(certFilePath, certFilePwd); 
     kms = kmf.getKeyManagers(); 
     /* 
     List<X509Certificate> certs = SslUtils.getClientCaCerts(kms); 
     TrustManagerFactory tmf = SslUtils.createTrustStore(Constants.CERT_CA_ALIAS, certs.get(0)); 
     tms = tmf.getTrustManagers(); 
     */ 
     tms = new TrustManager[1]; 
     tms[0] = new TrustServerCertAlways(); 
    } 

    @Override 
    public void init(Series<Parameter> parameters) { 
     log.debug("Not using parameters to initialize client SSL Context factory."); 
    } 

    @Override 
    public SSLContext createSslContext() throws Exception { 

     SSLContext ctx = SSLContext.getInstance(SslUtils.DEFAULT_SSL_PROTOCOL); 
     ctx.init(kms, tms, null); 
     return ctx; 
    } 

    static class TrustServerCertAlways implements X509TrustManager { 

     @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { 
      log.debug("Trusting all client certificates."); 
     } 

     @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { 
      log.debug("Trusting all server certificates."); 
     } 

     @Override public X509Certificate[] getAcceptedIssuers() { 
      log.debug("No accepted issuers."); 
      return null; 
     } 
    } 

} 

E infine la classe SslUtils contenente i metodi "leggere e ricostruire" (versione completa, tra cui "get indirizzo e-mail dal certificato" metodi è disponibile nel progetto Github già citato):

import java.io.InputStream; 
import java.net.Authenticator; 
import java.net.PasswordAuthentication; 
import java.nio.file.Files; 
import java.nio.file.Path; 
import java.security.KeyStore; 
import java.security.KeyStore.LoadStoreParameter; 
import java.security.cert.X509Certificate; 
import java.util.*; 

import javax.net.ssl.*; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

public class SslUtils { 

    private static final Logger log = LoggerFactory.getLogger(SslUtils.class); 

    /** 
    * List of SSL protocols (SSLv3, TLSv1.2, etc.). See also {@link SslUtils#DEFAULT_SSL_PROTOCOL}. 
    * <br>Documented at http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext 
    */ 
    public static final String[] SSL_PROTOCOLS = new String[] { "SSL", "SSLv2", "SSLv3", "TLS", "TLSv1", "TLSv1.1", "TLSv1.2" }; 

    /** 
    * Default SSL protocol to use ("TLSv1.2"). 
    */ 
    public static final String DEFAULT_SSL_PROTOCOL = "TLSv1.2"; 

    /** 
    * Creates a default SSL context with an empty key-store and the default JRE trust-store. 
    */ 
    public static SSLContext createDefaultSslContext() throws Exception { 
     return createSslContext(null, null, null, null); 
    } 
    /** 
    * Creates a default SSL socket factory. 
    * <br>All system properties related to trust/key-stores are ignored, eveything is done programmatically. 
    * This is because the Sun implementation reads the system-properties once and then caches the values. 
    * Among other things, this fails the unit tests. 
    * <br>For reference, the system properties (again, NOT USED): 
    * <br> - javax.net.ssl.trustStore (default cacerts.jks) 
    * <br> - javax.net.ssl.trustStorePassword 
    * <br>and for client certificate: 
    * <br> - javax.net.ssl.keyStore (set to "agent-cert.p12") 
    * <br> - javax.net.ssl.keyStoreType (set to "pkcs12") 
    * <br> - javax.net.ssl.keyStorePassword 
    * <br>See for a discussion: 
    * https://stackoverflow.com/questions/6340918/trust-store-vs-key-store-creating-with-keytool 
    * <br>See for client certificates in Java: 
    * https://stackoverflow.com/questions/1666052/java-https-client-certificate-authentication 
    * @param keyStoreFileName The name (ending with pfx) of the file with client certificates. 
    * @param trustStoreFileName The name (ending with jks) of the Java KeyStore with trusted (root) certificates. 
    * @return null or the SSLContext. 
    */ 
    public static SSLContext createSslContext(Path keyStoreFile, String keyStorePwd, 
      Path trustStoreFile, String trustStorePwd) throws Exception { 
     return createSslContext(keyStoreFile, keyStorePwd, trustStoreFile, trustStorePwd, DEFAULT_SSL_PROTOCOL); 
    } 

    /** 
    * See {@link #createSslContext(Path, String, Path, String)}. 
    * @param sslProtocol a value from {@link #SSL_PROTOCOLS}. 
    */ 
    public static SSLContext createSslContext(Path keyStoreFile, String keyStorePwd, 
      Path trustStoreFile, String trustStorePwd, String sslProtocol) throws Exception { 

     KeyManagerFactory kmf = loadKeyStore(keyStoreFile, keyStorePwd == null ? null : keyStorePwd.toCharArray()); 
     TrustManagerFactory tmf = loadTrustStore(trustStoreFile, trustStorePwd == null ? null : trustStorePwd.toCharArray()); 
     //set an Authenticator to generate username and password 
     SSLContext ctx = SSLContext.getInstance(sslProtocol); 
     ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 
     return ctx; 
    } 

    /** 
    * Calls {@link #createSslContextFromClientKeyStore(Path, String, Path, String)} with the {@link #DEFAULT_SSL_PROTOCOL}. 
    */ 
    public static SSLContext createSslContextFromClientKeyStore(Path keyStoreFile, String keyStorePwd, 
      String caAlias) throws Exception { 
     return createSslContextFromClientKeyStore(keyStoreFile, keyStorePwd, caAlias, DEFAULT_SSL_PROTOCOL); 
    } 

    /** 
    * Creates a SSL context from the given key-store containing a client certificate and a (CA) root certificate. 
    * The root certificate is set in the trust-store of the SSL context. 
    * @param keyStoreFileName key-store file name (ending with .pfx). 
    * @param keyStorePwd key-store password 
    * @param caAlias the alias to use for the CA (root) certificate (e.g. "mycaroot"). 
    * @param sslProtocol the ssl-protocol (e.g. {@link #DEFAULT_SSL_PROTOCOL}). 
    */ 
    public static SSLContext createSslContextFromClientKeyStore(Path keyStoreFile, String keyStorePwd, 
      String caAlias, String sslProtocol) throws Exception { 

     KeyManagerFactory kmf = loadKeyStore(keyStoreFile, keyStorePwd == null ? null : keyStorePwd.toCharArray()); 
     List<X509Certificate> certs = getClientCaCerts(kmf.getKeyManagers()); 
     if (certs.size() < 1) { 
      throw new Exception("Cannot find CA (root) certificate in key-managers from key store " + keyStoreFile.getFileName()); 
     } 
     TrustManagerFactory tmf = createTrustStore(caAlias, certs.get(0)); 
     SSLContext ctx = SSLContext.getInstance(sslProtocol); 
     ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 
     return ctx; 
    } 

    public static KeyManagerFactory loadKeyStore(Path storeFile) throws Exception { 
     return loadKeyStore(storeFile, null); 
    } 

    public static KeyManagerFactory loadKeyStore(Path storeFile, char[] storePwd) throws Exception { 
     return loadKeyStore(storeFile, storePwd, null, null); 
    } 

    public static KeyManagerFactory loadKeyStore(Path storeFile, char[] storePwd, 
      String storeType, String algorithm) throws Exception { 

     KeyManagerFactory kmf = null; 
     if (storeFile == null) { 
      kmf = loadKeyStore((InputStream)null, storePwd, storeType, algorithm); 
     } else { 
      try (InputStream storeIn = Files.newInputStream(storeFile)) { 
       kmf = loadKeyStore(storeIn, storePwd, storeType, algorithm); 
       log.info("Initialized certificate key-store from [" + storeFile.getFileName() + "]"); 
      } 
     } 
     return kmf; 
    } 

    public static KeyManagerFactory loadKeyStore(InputStream storeIn, char[] storePwd, 
      String storeType, String algorithm) throws Exception { 

     if (storePwd == null && storeIn != null) { 
      storePwd = "changeit".toCharArray(); 
      log.debug("Using default key store password."); 
     } 
     if (storeType == null) { 
      storeType = "pkcs12"; 
      log.debug("Using default key store type " + storeType); 
     } 
     if (algorithm == null) { 
      algorithm = KeyManagerFactory.getDefaultAlgorithm(); // "SunX509" 
      log.debug("Using default key store algorithm " + algorithm); 
     } 
     KeyManagerFactory kmf = null; 
     KeyStore keyStore = loadStore(storeIn, storePwd, storeType); 
     kmf = KeyManagerFactory.getInstance(algorithm); 
     kmf.init(keyStore, storePwd); 
     if (storeIn == null) { 
      log.info("Initialized a default certificate key-store"); 
     } 
     return kmf; 
    } 

    /** 
    * Creates a trust-store with the given CA (root) certificate. 
    * @param certAlias the alias for the certificate (e.g. "mycaroot") 
    * @param caCert the CA (root) certificate 
    * @return an initialized trust manager factory. 
    */ 
    public static TrustManagerFactory createTrustStore(String certAlias, X509Certificate caCert) throws Exception { 

     KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
     ks.load((LoadStoreParameter)null); // must initialize the key-store 
     ks.setCertificateEntry(certAlias, caCert); 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init(ks); 
     return tmf; 
    } 

    public static TrustManagerFactory loadTrustStore(Path storeFile) throws Exception { 
     return loadTrustStore(storeFile, null); 
    } 

    public static TrustManagerFactory loadTrustStore(Path storeFile, char[] storePwd) throws Exception { 
     return loadTrustStore(storeFile, storePwd, null, null); 
    } 

    public static TrustManagerFactory loadTrustStore(Path storeFile, char[] storePwd, 
      String storeType, String algorithm) throws Exception { 

     TrustManagerFactory tmf = null; 
     if (storeFile == null) { 
      tmf = loadTrustStore((InputStream)null, storePwd, storeType, algorithm); 
     } else { 
      try (InputStream storeIn = Files.newInputStream(storeFile)) { 
       tmf = loadTrustStore(storeIn, storePwd, storeType, algorithm); 
      } 
      log.info("Initialized certificate trust-store from [" + storeFile.getFileName() + "]"); 
     } 
     return tmf; 
    } 

    public static TrustManagerFactory loadTrustStore(InputStream storeIn, char[] storePwd, 
      String storeType, String algorithm) throws Exception { 

     if (storePwd == null && storeIn != null) { 
      storePwd = "changeit".toCharArray(); 
      log.debug("Using default trust store password."); 
     } 
     if (storeType == null) { 
      storeType = KeyStore.getDefaultType(); 
      log.debug("Using default trust store type " + storeType); 
     } 
     if (algorithm == null) { 
      algorithm = TrustManagerFactory.getDefaultAlgorithm(); 
      log.debug("Using default trust store algorithm " + algorithm); 
     } 
     TrustManagerFactory tmf = null; 
     KeyStore trustStore = loadStore(storeIn, storePwd, storeType); 
     tmf = TrustManagerFactory.getInstance(algorithm); 
     tmf.init(trustStore); 
     if (storeIn == null) { 
      log.info("Initialized a default certificate trust-store"); 
     } 
     return tmf; 
    } 

    /** 
    * Creates a default trust store containing the JRE certificates in {@code JAVA_HOME\lib\security\cacerts.jks} 
    * <br>To view loaded certificates call 
    * <br>{@code System.setProperty("javax.net.debug", "ssl,trustmanager");} 
    * <br>before calling this method. 
    */ 
    public static TrustManagerFactory createDefaultTrustStore() throws Exception { 

     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init((KeyStore)null); 
     return tmf; 
    } 

    /** 
    * @param in if null, null is returned. 
    */ 
    public static KeyStore loadStore(InputStream in, char[] pwd, String type) throws Exception { 

     if (in == null) { 
      return null; 
     } 
     KeyStore ks = KeyStore.getInstance(type); 
     ks.load(in, pwd); 
     return ks; 
    } 

    /** 
    * Finds any CA (root) certificates present in client certificate chains. 
    * <br>Uses {@link #getClientAliases(KeyManager)} 
    * @param kms key-managers (from a key-store). 
    * @return an empty list or a list containing CA (root) certificates. 
    */ 
    public static List<X509Certificate> getClientCaCerts(KeyManager[] kms) { 

     List<X509Certificate> caCerts = new LinkedList<X509Certificate>(); 
     for (int i = 0; i < kms.length; i++) { 
      if (!(kms[i] instanceof X509KeyManager)) { 
       continue; 
      } 
      X509KeyManager km = (X509KeyManager) kms[i]; 
      List<String> aliases = getClientAliases(km); 
      for (String alias: aliases) { 
       X509Certificate[] cchain = km.getCertificateChain(alias); 
       if (cchain == null || cchain.length < 2) { 
        continue; 
       } 
       // first certificate in chain is the user certificate 
       // last certificate is the CA (root certificate). 
       caCerts.add(cchain[cchain.length-1]); 
       if (log.isDebugEnabled()) { 
        log.debug("Found 1 root certificate from client certificate alias " + alias); 
       } 
      } 
     } 
     return caCerts; 
    } 

    /** 
    * List of key types for client certificate aliases, used in {@link #getAliases(KeyManager)} 
    * <br>List is documented at 
    * http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#jssenames 
    */ 
    public static final String[] KEY_TYPES = new String[] {"RSA", "DSA", "DH_RSA", "DH_DSA", "EC", "EC_EC", "EC_RSA" }; 

    /** 
    * Searches for client aliases in the given key-manager. 
    * Does nothing when the given key-manager is not an instance of {@link X509KeyManager}. 
    * @return an empty list or a list containing client aliases found in the key-manager. 
    */ 
    public static List<String> getClientAliases(KeyManager keyManager) { 

     List<String> aliases = new LinkedList<String>(); 
     if (keyManager instanceof X509KeyManager) { 
      X509KeyManager km = (X509KeyManager) keyManager; 
      for (String keyType: KEY_TYPES) { 
       String[] kmAliases = km.getClientAliases(keyType, null); 
       if (kmAliases != null) { 
        for (String alias: kmAliases) { 
         if (!isEmpty(alias)) { 
          aliases.add(alias); 
         } 
        } 
       } 
      } // for keytypes 
     } 
     return aliases; 
    } 

    /** 
    * Sets the default authenticator which can be used for example with http-request that require basic authoriation. 
    * <br>See also {@link Authenticator#setDefault(Authenticator)}. 
    */ 
    public static void setDefaultAuthenticator(final String userName, final char[] pwd) throws Exception { 

     Authenticator auth = new Authenticator() { 
      @Override 
      protected PasswordAuthentication getPasswordAuthentication() { 
       return new PasswordAuthentication(userName, pwd); 
      } 
     }; 
     Authenticator.setDefault(auth); 
    } 

    /** 
    * @return true if s is not null and not empty after trimming, false otherwise. 
    */ 
    public static boolean isEmpty(String s) { return (s == null || s.trim().isEmpty()); } 

} 

Su un lato-nodo: Java sta passando il tipo predefinito chiavi da JKS a PKCS12 (vedi JEP 229).

2

sto usando lo stesso .p12 file in server e client

Questo è già un errore. Il client e il server hanno identità diverse e non dovrebbero avere la stessa chiave privata, chiave pubblica o certificato.

vi consiglio di fosso tutta la roba OpenSSL e iniziare di nuovo con la keytool come segue:

  1. Sul server, generare una coppia di chiavi, e una richiesta di certificato; farlo firmare; importare la catena di certificati del firmatario con l'opzione -trustcacerts; e importa il certificato firmato usando lo stesso alias che hai usato durante la creazione della coppia di chiavi e CSR.
  2. Al client, idem, ma utilizzando (ovviamente) un diverso file di archivio chiavi.
  3. Il gioco è fatto.Dimenticatevi di

    • OpenSSL
    • PKCS # 12
    • autofirmati certificati
    • tutte le forme di trustAllCerts, personalizzati TrustManagers, e codice personalizzato di qualsiasi natura
    • utilizzando la stessa coppia di chiavi/certificato per server e client
    • importazione del certificato del server sul client e vice versa
    • eventuali proprietà di sistema diverse da quelle che identificano javax.net.ssl.keyStore e javax.net.ssl.keyStorePassword
    • impostando una password sulla chiave corrispondente o sul certificato firmato importato.

passaggi (1) e (2) sono come si intende fare. Parti da quelli e sei nei guai e nella lotta.

+0

Mi piace la tua risposta, ma sembra un po 'troppo ampia. Il problema può probabilmente essere risolto aggiornando il file p12, quindi ricominciare da capo e pagare per i nuovi certificati sembra un po 'eccessivo. Anche se lo farebbe risparmiare un sacco di guai la prossima volta che vorrà farlo.La danza openssl è ancora ampiamente documentata come la strada da percorrere, purtroppo, probabilmente perché è valida anche per gli utenti non java. – greyfairer

+0

@greyfairer Non c'è alcuna prova nella domanda che abbia pagato per qualcosa ancora. La domanda ha ogni aspetto dei certificati autofirmati, fino al codice 'TrustManager' insicuro, che altrimenti sarebbe inutile. Ma anche se lo ha, dovrà comunque pagare almeno un nuovo certificato, poiché l'utilizzo dello stesso per entrambi i fini non è valido, e l'utilizzo di OpenSSL in un contesto Java è solo un PITA evitabile per il momento. – EJP

2

Probabilmente non hai aggiunto la catena completa di certificati nel tuo keystore e hai incluso solo la coppia di chiavi. In tal caso, il client riceve solo la chiave pubblica, ma non può validare se tale chiave può essere considerata attendibile. La catena di certificati è lì per essere in grado di verificare se le firme sulla chiave pubblica corrispondono e portare a un'autorità di certificazione attendibile.

Vedere per esempio: Adding certificate chain to p12(pfx) certificate

openssl pkcs12 -in certificate.p12 -out clientcert.pem -nodes -clcerts 
openssl x509 -in trusted_ca.cer -inform DER -out trusted_ca.pem 
openssl x509 -in root_ca.cer -inform DER -out root_ca.pem 
cat clientcert.pem trusted_ca.pem root_ca.pem >> clientcertchain.pem 
openssl pkcs12 -export -in clientcertchain.pem -out clientcertchain.pfx 

si può fare il modo in cui Java, anche, utilizzando per esempio portecle: http://portecle.sourceforge.net/import-ca-reply.html, ma è anche necessario combinare la catena di certificati in un file da importare. Basta copiare e incollare tutti i certificati uno dopo l'altro, iniziando dal proprio e terminando con la CA principale.

In questo modo, il file pfx risultante può essere utilizzato sul server per restituire la catena di certificati al client.