Comment puis-je faire TLS avec BouncyCastle?

est-ce que quelqu'un connaît des exemples de TLS avec BouncyCastle? J'ai été surpris par leur absence sur Internet. Si il n'y a vraiment aucune, nous allons recueillir les réponses.

16
demandé sur Tshepang 2013-08-05 22:29:41

3 réponses

il s'agit d'un exemple très basique, avec authentification serveur seulement et autocotisation cert. Le code est basé sur BC 1.49, principalement L'API leightweight:

ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
final KeyPair keyPair = ...
final Certificate bcCert = new Certificate(new org.spongycastle.asn1.x509.Certificate[] {
    new X509V3CertificateStrategy().selfSignedCertificateHolder(keyPair).toASN1Structure()}); 
while (true) {
    Socket socket = serverSocket.accept();
    TlsServerProtocol tlsServerProtocol = new TlsServerProtocol(
    socket.getInputStream(), socket.getOutputStream(), secureRandom);
    tlsServerProtocol.accept(new DefaultTlsServer() {
        protected TlsSignerCredentials getRSASignerCredentials() throws IOException {
            return tlsSignerCredentials(context);
        }               
    });      
    new PrintStream(tlsServerProtocol.getOutputStream()).println("Hello TLS");
}

private TlsSignerCredentials tlsSignerCredentials(TlsContext context) throws IOException {
    return new DefaultTlsSignerCredentials(context, bcCert,
            PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded()));                
}

voici le code client:

Socket socket = new Socket(<server IP>, SERVER_PORT);
TlsClientProtocol tlsClientProtocol = new TlsClientProtocol(    
    socket.getInputStream(), socket.getOutputStream());
tlsClientProtocol.connect(new DefaultTlsClient() {          
    public TlsAuthentication getAuthentication() throws IOException {
        return new ServerOnlyTlsAuthentication() {                  
            public void notifyServerCertificate(Certificate serverCertificate) throws IOException {
                validateCertificate(serverCertificate);
            }
        };
    }
});
String message = new BufferedReader(
    new InputStreamReader(tlsClientProtocol.getInputStream())).readLine();

vous devez utiliser le flux d'entrée et de sortie de tlsClient/ServerProtocol pour lire et écrire des données cryptées (par exemple tlsClientProtocol.getInputStream()). Sinon, si vous avez utilisé par exemple douille.getOutputStream (), vous écririez simplement non crypté données.

comment implémenter validateCertificate? Je suis à l'aide de certificats auto-signés. Cela signifie que je viens de les rechercher dans le magasin de clés sans aucune chaîne de certificat. C'est comme ça que je crée le key store:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
X509Certificate certificate = ...;
keyStore.setCertificateEntry(alias, certificate);

Et c'est la validation:

private void validateCertificate(org.spongycastle.crypto.tls.Certificate cert) throws IOException, CertificateException, KeyStoreException {
    byte[] encoded = cert.getCertificateList()[0].getEncoded();
    java.security.cert.Certificate jsCert = 
        CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(encoded));
    String alias = keyStore.getCertificateAlias(jsCert);
    if(alias == null) {
        throw new IllegalArgumentException("Unknown cert " + jsCert);
    }
}

ce qui est plutôt déroutant, ce sont les trois classes de certificats différentes. Vous devez convertir entre eux comme indiqué ci-dessus.

12
répondu Jakub Adamek 2013-08-09 17:25:01

scénario: notre serveur de production utilise JDK1.6. Cependant, le serveur client est mis à jour pour ne communiquer que dans TLS 1.2. La Communication SSL entre les deux serveurs est rompue. Mais nous ne pouvons pas simplement mettre à jour JDK6 en 8 (qui supporte TLS 1.2 par défaut) parce que cela causera d'autres problèmes de compatibilité avec les bibliothèques.

le code d'échantillon suivant utilise jdk1.6.0_45 et bcprov-jdk15on-153.jar (Bouncy Castle SIGNED JAR FILES) pour se connecter à n'importe quel serveur en utilisant TLS.

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

import org.bouncycastle.crypto.tls.CertificateRequest;
import org.bouncycastle.crypto.tls.DefaultTlsClient;
import org.bouncycastle.crypto.tls.TlsAuthentication;
import org.bouncycastle.crypto.tls.TlsClientProtocol;
import org.bouncycastle.crypto.tls.TlsCredentials;

public class TestHttpClient {
    // Reference: http://boredwookie.net/index.php/blog/how-to-use-bouncy-castle-lightweight-api-s-tlsclient/
    //            bcprov-jdk15on-153.tar\src\org\bouncycastle\crypto\tls\test\TlsClientTest.java
    public static void main(String[] args) throws Exception {
        java.security.SecureRandom secureRandom = new java.security.SecureRandom();
        Socket socket = new Socket(java.net.InetAddress.getByName("www.google.com"), 443);
        TlsClientProtocol protocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(),secureRandom);
        DefaultTlsClient client = new DefaultTlsClient() {
            public TlsAuthentication getAuthentication() throws IOException {
                TlsAuthentication auth = new TlsAuthentication() {
                    // Capture the server certificate information!
                    public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate) throws IOException {
                    }

                    public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException {
                        return null;
                    }
                };
                return auth;
            }
        };
        protocol.connect(client);

        java.io.OutputStream output = protocol.getOutputStream();
        output.write("GET / HTTP/1.1\r\n".getBytes("UTF-8"));
        output.write("Host: www.google.com\r\n".getBytes("UTF-8"));
        output.write("Connection: close\r\n".getBytes("UTF-8")); // So the server will close socket immediately.
        output.write("\r\n".getBytes("UTF-8")); // HTTP1.1 requirement: last line must be empty line.
        output.flush();

        java.io.InputStream input = protocol.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        String line;
        while ((line = reader.readLine()) != null)
        {
            System.out.println(line);
        }
    }
}

exemple de sortie montre que JDK 6 peut obtenir la page du serveur dans TLS, plutôt qu'une Exception SSL:

HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: https://www.google.com.sg/?gfe_rd=cr&ei=WRgeVovGEOTH8Afcx4XYAw
Content-Length: 263
Date: Wed, 14 Oct 2015 08:54:49 GMT
Server: GFE/2.0
Alternate-Protocol: 443:quic,p=1
Alt-Svc: quic="www.google.com:443"; p="1"; ma=600,quic=":443"; p="1"; ma=600
Connection: close

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://www.google.com.sg/?gfe_rd=cr&amp;ei=WRgeVovGEOTH8Afcx4XYAw">here</A>.
</BODY></HTML>
7
répondu oraclesoon 2015-10-15 02:08:04

un exemple de plus, construit sur le dessus de la réponse d'auth du serveur seulement: TLS avec des certs auto-signés avec l'authentification du client (je montre juste les parties modifiées). C'est la partie serveur:

tlsServerProtocol.accept(new DefaultTlsServer() {
    protected TlsSignerCredentials getRSASignerCredentials() throws IOException {
        return tlsSignerCredentials(context);
    }
    public void notifyClientCertificate(Certificate clientCertificate) throws IOException {
        validateCertificate(clientCertificate);
    }
    public CertificateRequest getCertificateRequest() {
        return new CertificateRequest(new short[] { ClientCertificateType.rsa_sign },  new Vector<Object>());
    }
});        

Et c'est la partie client:

tlsClientProtocol.connect(new DefaultTlsClient() {            
    public TlsAuthentication getAuthentication() throws IOException {
        return new TlsAuthentication() {                    
            public void notifyServerCertificate(Certificate serverCertificate) throws IOException {
                validateCertificate(serverCertificate);
            }
            public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException {
                return tlsSignerCredentials(context);
            }
        };
    }
});
5
répondu Jakub Adamek 2013-08-09 17:25:30