bc-java
bc-java copied to clipboard
Add GMSSL support
GMSSL follow the specification《GMT 0024-2014 SSL VPN技术规范》
GMSSL a variant of TLS1.1(RFC4346)
Use independent protocol version number 0x0101 Cipher suite using GM algorithm SM2_SM4_SM3
- SM2 for auth
- SM4 for encrypt
- SM3 for hmac
certficate message contain two cert, first for sign second for encypt.
more difference see https://blog.csdn.net/q1009020096/article/details/114321986?spm=1001.2014.3001.5501
For testing and use, see:
- tls/src/test/java/org/bouncycastle/tls/test/GMSimpleSSLSocketFactoryTest.java
- tls/src/test/java/org/bouncycastle/tls/test/GMSSLClientTest.java
- tls/src/test/java/org/bouncycastle/tls/test/GMSSLServerTest.java
more:
- GMSSL HTTPs can use MeSign browser access
- Debug and filter could use wireshark-gm
reference:
[1]. IETF. RFC4346 . 2006 [2]. 密码行业标准化技术委员会 . GMT 0024-2014 SSL VPN技术规范 . 2014
Apache HTTPClient GMSSL Example:
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketFactory;
import java.security.Security;
/**
* GMSSL Http Client test
*
* @author Cliven
* @since 2021-02-05 13:25:06
*/
public class GMHttpClient {
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
GMSimpleSSLSocketFactory factory = new GMSimpleSSLSocketFactory();
SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(factory, new NoopHostnameVerifier());
HttpClient client = HttpClientBuilder.create()
.setSSLSocketFactory(sf)
.build();
final HttpResponse response = client.execute(new HttpGet("https://127.0.0.1:5557"));
response.getEntity().writeTo(System.out);
}
}
Just one thing, we do not allow author tags (which I'll admit can be annoying as most IDEs now insist on inserting one when you create a file, here we take the attitude that if anything's broken, it's all our problem). I think we can accept this otherwise. I just wanted to check that was understood.
Just one thing, we do not allow author tags (which I'll admit can be annoying as most IDEs now insist on inserting one when you create a file, here we take the attitude that if anything's broken, it's all our problem). I think we can accept this otherwise. I just wanted to check that was understood.
I have removed all @author
tags. Is there anything else I need to adjust?
Thanks. Just one other thing, is this meant to be compliant with RFC 8998? I just noticed there was a comment concerning TLS 1.1. at the top.
Okay, I've done a bit more reading. This is different from RFC 8998 isn't it? In some ways it's not really TLS is it, it's more a TLS like protocol. Would that be correct?
Thanks. Just one other thing, is this meant to be compliant with RFC 8998? I just noticed there was a comment concerning TLS 1.1. at the top.
Okay, I've done a bit more reading. This is different from RFC 8998 isn't it? In some ways it's not really TLS is it, it's more a TLS like protocol. Would that be correct?
Correct.
It is more like a dialect based on a standard language.
Although it is called SSL, it is closer to TLS because it is modified from TLS RFC4346. Most of the content is the same as TLS1.1, except that ShangMi (SM) Cipher is used.
I personally think that it may be more appropriate to call it GMTLS, but it is called GMSSL in GMT0024-2012, which is just a difference in name.
The latest version of Chinese cipher algorithm has been added in TLS1.3, I remember the protocol number is RFC8998 ShangMi (SM) Cipher Suites for TLS 1.3, they are not the same thing.
RFC8998 describes the addition of ShangMi (SM) algorithm suite to TLS1.3, while and GMT0024-2012 is an independent revision branch based on TLS1.1.
One question that has come up is how does the Certificate message work in the SM2 key exchange algorithm. There appear to be 2 end-entity certificates in the message, are there going to be others in the chain and do the two certificate have a common issuer?
One question that has come up is how does the Certificate message work in the SM2 key exchange algorithm. There appear to be 2 end-entity certificates in the message, are there going to be others in the chain and do the two certificate have a common issuer?
Yes, the two certificate have same issuer, and the server certificate message contains only two certificates without other certificate chains, one for signing and the other is used for encryption
GMT0024-2012 6.4.4.2 Server Certificate Message struct as follows:
Server certificate: The signing certificate comes first, and the encryption certificate comes behind.
If you need to a SM2 server certificate, you need to submit a certificate request to CA, then you will receive:
- two
certificates
(one cert for signing, other for encryption ) - one
SignedAndEnvelopedData
(encrypted a SM2 key pair inside), you can use priviate key of sign cert to decrypt this.
It is different from RSA in TLS1.1. In TLS1.1, both signature and encryption use the same pair of secret keys, while GMSSL uses two keypairs to work independently.
The handshake message flow is basically the same as TLS1.1:
I drew a picture about the key exchange of SM2_SM4_SM3 cipher suite below:
The picture only describes the key exchange related messages, and does not completely cover the entire handshake process.
We've finished analysing the patches. It's been quite a valuable exercise in the sense that it has shown up some shortcomings in our approach. We feel that treating GM SSL as a branch of TLS 1.1 may be the best way to go also. You'll notice more and more conflicts with your patch as we do the merge. We'll let you know when we are done, hopefully at that point we'll something you can simply start using. There is no need to make any changes at the moment.
We've finished analysing the patches. It's been quite a valuable exercise in the sense that it has shown up some shortcomings in our approach. We feel that treating GM SSL as a branch of TLS 1.1 may be the best way to go also. You'll notice more and more conflicts with your patch as we do the merge. We'll let you know when we are done, hopefully at that point we'll something you can simply start using. There is no need to make any changes at the moment.
ok, thanks
We've finished analysing the patches. It's been quite a valuable exercise in the sense that it has shown up some shortcomings in our approach. We feel that treating GM SSL as a branch of TLS 1.1 may be the best way to go also. You'll notice more and more conflicts with your patch as we do the merge. We'll let you know when we are done, hopefully at that point we'll something you can simply start using. There is no need to make any changes at the moment.
When will this branch be merged
We're still working through it. You should see bits of it showing up now though.
Apache HTTPClient GMSSL Example:
import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; import org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketFactory; import java.security.Security; /** * GMSSL Http Client test * * @author Cliven * @since 2021-02-05 13:25:06 */ public class GMHttpClient { public static void main(String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider()); GMSimpleSSLSocketFactory factory = new GMSimpleSSLSocketFactory(); SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(factory, new NoopHostnameVerifier()); HttpClient client = HttpClientBuilder.create() .setSSLSocketFactory(sf) .build(); final HttpResponse response = client.execute(new HttpGet("https://127.0.0.1:5557")); response.getEntity().writeTo(System.out); } }
请问下 示例中客户端的证书改如何设置?客户端连接的时候不是传递ca证书、签名证书|密钥、加密证书|密钥即可,但是 GMSSKeyParameters 构造函数很不明确
Apache HTTPClient GMSSL Example:
import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; import org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketFactory; import java.security.Security; /** * GMSSL Http Client test * * @author Cliven * @since 2021-02-05 13:25:06 */ public class GMHttpClient { public static void main(String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider()); GMSimpleSSLSocketFactory factory = new GMSimpleSSLSocketFactory(); SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(factory, new NoopHostnameVerifier()); HttpClient client = HttpClientBuilder.create() .setSSLSocketFactory(sf) .build(); final HttpResponse response = client.execute(new HttpGet("https://127.0.0.1:5557")); response.getEntity().writeTo(System.out); } }
请问下 示例中客户端的证书改如何设置?客户端连接的时候不是传递ca证书、签名证书|密钥、加密证书|密钥即可,但是 GMSSKeyParameters 构造函数很不明确
目前我还没有时间去处理客户端对服务端证书的验证,以及双向身份认证,目前的这个分支仅仅实现了单向的gmssl
Apache HTTPClient GMSSL Example:
import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; import org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketFactory; import java.security.Security; /** * GMSSL Http Client test * * @author Cliven * @since 2021-02-05 13:25:06 */ public class GMHttpClient { public static void main(String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider()); GMSimpleSSLSocketFactory factory = new GMSimpleSSLSocketFactory(); SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(factory, new NoopHostnameVerifier()); HttpClient client = HttpClientBuilder.create() .setSSLSocketFactory(sf) .build(); final HttpResponse response = client.execute(new HttpGet("https://127.0.0.1:5557")); response.getEntity().writeTo(System.out); } }
请问下 示例中客户端的证书改如何设置?客户端连接的时候不是传递ca证书、签名证书|密钥、加密证书|密钥即可,但是 GMSSKeyParameters 构造函数很不明确
目前我还没有时间去处理客户端对服务端证书的验证,以及双向身份认证,目前的这个分支仅仅实现了单向的gmssl
访问 https://demo.gmssl.cn 没有问题,但有一些使用国密证书的URL会抛出这样的握手失败的异常,造成连接失败: 出现问题的URL使用了SM4_CBC(加密)+HMAC_SM3(数字摘要)+SM2(密钥交换) 密码套件。好奇怪
Exception in thread "main" org.bouncycastle.tls.TlsFatalAlert: handshake_failure(40)
at org.bouncycastle.tls.AbstractTlsPeer.notifySecureRenegotiation(AbstractTlsPeer.java:121)
at org.bouncycastle.tls.TlsClientProtocol.processServerHello(TlsClientProtocol.java:1216)
at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(TlsClientProtocol.java:497)
at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(TlsProtocol.java:629)
at org.bouncycastle.tls.TlsProtocol.processRecord(TlsProtocol.java:519)
at org.bouncycastle.tls.RecordStream.readRecord(RecordStream.java:245)
at org.bouncycastle.tls.TlsProtocol.safeReadRecord(TlsProtocol.java:777)
at org.bouncycastle.tls.TlsProtocol.blockForHandshake(TlsProtocol.java:370)
at org.bouncycastle.tls.TlsClientProtocol.connect(TlsClientProtocol.java:86)
at org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocket.makeHandshake(GMSimpleSSLSocket.java:181)
at org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketWrap.startHandshake(GMSimpleSSLSocketWrap.java:271)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:394)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
UPDATE:找到原因了,在TlsClientProtocol类里发送了重新协商请求,如果server端不支持重新协商那么就会Alert40。目前暂时注释掉这行即可。
// TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming
tlsClient.notifySecureRenegotiation(securityParameters.isSecureRenegotiation());
Hi @dghgit , Do you have plan to merge this pr or there is another solution to support GMSSL from official?