Java 如何使用 BouncyCastle 进行 TLS?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18065170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I do TLS with BouncyCastle?
提问by Jakub Adamek
Does anybody know about examples of TLS with BouncyCastle? I was surprised by the lack of them on Internet. If there are really none, let's collect them as answers.
有人知道使用 BouncyCastle 的 TLS 示例吗?我对互联网上缺少它们感到惊讶。如果真的没有,让我们收集它们作为答案。
回答by Jakub Adamek
One more example, built on top of the server-only auth answer: TLS with self-signed certs with client authentication (I am showing just the changed parts). This is the server part:
还有一个例子,建立在仅服务器身份验证的基础上:TLS with self-signed certs with client authentication(我只展示了更改的部分)。这是服务器部分:
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>());
}
});
And this is the client part:
这是客户端部分:
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);
}
};
}
});
回答by Jakub Adamek
This is a very basic example, with server-only authentication and self-signed cert. The code is based on BC 1.49, mostly leightweight API:
这是一个非常基本的示例,具有仅服务器身份验证和自签名证书。代码基于 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");
}
where
在哪里
private TlsSignerCredentials tlsSignerCredentials(TlsContext context) throws IOException {
return new DefaultTlsSignerCredentials(context, bcCert,
PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded()));
}
This is the client code:
这是客户端代码:
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();
You need to use the input and output stream from tlsClient/ServerProtocol to read and write encrypted data (e.g. tlsClientProtocol.getInputStream()). Otherwise, if you used e.g. socket.getOutputStream(), you would just write unencrypted data.
您需要使用来自 tlsClient/ServerProtocol 的输入和输出流来读取和写入加密数据(例如 tlsClientProtocol.getInputStream())。否则,如果您使用例如 socket.getOutputStream(),您将只写入未加密的数据。
How to implement validateCertificate? I am using self-signed certificates. This means I just look them up in the key-store without any certificate chains. This is how I create the key store:
如何实现validateCertificate?我正在使用自签名证书。这意味着我只是在没有任何证书链的情况下在密钥库中查找它们。这就是我创建密钥库的方式:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
X509Certificate certificate = ...;
keyStore.setCertificateEntry(alias, certificate);
And this is the 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);
}
}
What is rather confusing, are the three different Certificate classes. You have to convert between them as shown above.
令人困惑的是三个不同的证书类。您必须在它们之间进行转换,如上所示。
回答by oraclesoon
Scenario: Our production server is using JDK1.6. However customer server is upgraded to only communicate in TLS 1.2. SSL Communication between both servers is broken. But we cannot simply upgrade JDK6 to 8 (which is supporting TLS 1.2 by default) because this will cause other libraries compatibility issue.
场景:我们的生产服务器使用的是JDK1.6。但是,客户服务器已升级为仅在 TLS 1.2 中进行通信。两台服务器之间的 SSL 通信中断。但是我们不能简单地将JDK6升级到8(默认支持TLS 1.2),因为这会导致其他库兼容性问题。
The following sample code uses jdk1.6.0_45 and bcprov-jdk15on-153.jar (Bouncy Castle SIGNED JAR FILES) to connect to any server using TLS.
以下示例代码使用 jdk1.6.0_45 和 bcprov-jdk15on-153.jar(Bouncy Castle 签名 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);
}
}
}
Sample output shows that JDK 6 can obtain the server page in TLS, rather than some SSL Exception:
示例输出显示 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
<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&ei=WRgeVovGEOTH8Afcx4XYAw">here</A>.
</BODY></HTML>