【问题标题】:How do I do TLS with BouncyCastle?如何使用 BouncyCastle 进行 TLS?
【发布时间】:2013-08-05 18:29:41
【问题描述】:

有人知道带有 BouncyCastle 的 TLS 示例吗?我对互联网上缺少它们感到惊讶。如果真的没有,让我们收集它们作为答案。

【问题讨论】:

    标签: java ssl certificate bouncycastle x509


    【解决方案1】:

    这是一个非常基本的示例,具有仅服务器身份验证和自签名证书。代码基于 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 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();
    

    您需要使用来自 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);
        }
    }
    

    比较令人困惑的是三个不同的证书类。您必须在它们之间进行转换,如上所示。

    【讨论】:

    • 嘿,您愿意更新此答案以使用最新的 bouncycastle 版本吗?特别是,我回顾了 git 历史,它似乎并不存在 X509V3CertificateStrategy 类和 selfSignedCertificateHolder 方法。
    【解决方案2】:

    场景:我们的生产服务器使用 JDK1.6。但是,客户服务器已升级为仅在 TLS 1.2 中进行通信。两台服务器之间的 SSL 通信中断。但是我们不能简单地将JDK6升级到8(默认支持TLS 1.2),因为这会导致其他库的兼容性问题。

    以下示例代码使用 jdk1.6.0_45 和 bcprov-jdk15on-153.jar(Bouncy Castle SIGNED JAR 文件)使用 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 Exception:

    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>
    

    【讨论】:

    • 我坚持使用 Java 5 并试图通过 BouncyCastle 完成这项工作,但您的代码为不同的 url 提供了异常:“内部 TLS 错误,这可能是一次攻击”,知道如何解决它?
    • 我知道我在踢死马,但我发现自己需要在上面描述的确切代码场景中将 URI 添加到 URL 的末尾。我已经浏览了 BC 和 Socket 的 Javadocs 并且没有看到我缺少写入附加到 URL 的特定 URI 的内容,例如:google.com/something/else
    • @Bandolier2k output.write("GET /something/else HTTP/1.1\r\n".getBytes("UTF-8"));
    【解决方案3】:

    还有一个示例,建立在仅服务器身份验证答案之上:带有客户端身份验证的自签名证书的 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<Object>());
        }
    });        
    

    这是客户端部分:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 2013-09-22
      • 1970-01-01
      • 2020-03-02
      • 1970-01-01
      相关资源
      最近更新 更多