Как мне сделать TLS с BouncyCastle?

Кто-нибудь знает о примерах TLS с BouncyCastle? Я был удивлен отсутствием их в Интернете. Если их действительно нет, давайте соберем их в качестве ответов.

Это очень простой пример: с аутентификацией только на сервере и самозаверяющим сертификатом. Код основан на BC 1.49, в основном, с легким API:

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())); } 

Это код клиента:

 Socket socket = new Socket(, 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(); 

Для чтения и записи зашифрованных данных необходимо использовать stream ввода и вывода из tlsClient / ServerProtocol (например, tlsClientProtocol.getInputStream ()). В противном случае, если вы использовали, например, socket.getOutputStream (), вы просто записывали бы незашифрованные данные.

Как реализовать validateCertificate? Я использую самозаверяющие сертификаты. Это означает, что я просто просматриваю их в хранилище ключей без цепей сертификатов. Вот как я создаю хранилище ключей:

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

И это подтверждение:

 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); } } 

Что довольно запутанно, это три разных classа сертификатов. Вы должны преобразовать между ними, как показано выше.

Сценарий: На нашем производственном сервере используется JDK1.6. Однако клиентский сервер обновляется только для связи в TLS 1.2. SSL-связь между обоими серверами нарушена. Но мы не можем просто обновить JDK6 до 8 (который по умолчанию поддерживает TLS 1.2), потому что это вызовет проблему совместимости других библиотек.

Следующий пример кода использует jdk1.6.0_45 и bcprov-jdk15on-153.jar (Bouncy Castle SIGNED JAR FILES) для подключения к любому серверу с использованием 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); } } } 

Пример вывода показывает, что JDK 6 может получить серверную страницу в TLS, а не какое-то исключение 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  302 Moved 

302 Moved

The document has moved here.

Еще один пример, построенный на основе только ответа на сервер: TLS с самозаверяющими сертификатами с аутентификацией клиента (я показываю только измененные части). Это часть сервера:

 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()); } }); 

И это клиентская часть:

 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); } }; } }); 
Interesting Posts
Давайте будем гением компьютера.