diff --git a/core/src/main/java/org/bouncycastle/asn1/gm/SM2Cipher.java b/core/src/main/java/org/bouncycastle/asn1/gm/SM2Cipher.java new file mode 100644 index 0000000000..5a9b149a74 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/asn1/gm/SM2Cipher.java @@ -0,0 +1,180 @@ +package org.bouncycastle.asn1.gm; + +import org.bouncycastle.asn1.*; +import org.bouncycastle.util.BigIntegers; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Enumeration; + +/** + * GMT 0009-2012 + *
+ * sm2 encrypted data specific struct
+ *
+ *
+ * @since 2021-03-10 13:28:12
+ */
+public class SM2Cipher extends ASN1Object
+{
+ /*
+ * SM2Cipher ::== SEQUENCE{
+ * XCoordinate INTEGER, --X Portion
+ * YCoordinate INTEGER, --Y Portion
+ * HASH OCTET STRING SIZE(32), --Plaintext sm3 hash
+ * CipherText OCTET STRING --CipherText
+ * }
+ */
+
+ private ASN1Integer xCoordinate;
+ private ASN1Integer yCoordinate;
+ private ASN1OctetString hash;
+ private ASN1OctetString cipherText;
+
+ public SM2Cipher()
+ {
+ super();
+ }
+
+ public SM2Cipher(ASN1Sequence seq)
+ {
+ Enumeration> e = seq.getObjects();
+ xCoordinate = ASN1Integer.getInstance(e.nextElement());
+ yCoordinate = ASN1Integer.getInstance(e.nextElement());
+ hash = ASN1OctetString.getInstance(e.nextElement());
+ cipherText = ASN1OctetString.getInstance(e.nextElement());
+ }
+
+ public static SM2Cipher getInstance(Object o)
+ {
+ if(o instanceof SM2Cipher)
+ {
+ return (SM2Cipher) o;
+ }
+ else if(o != null)
+ {
+ return new SM2Cipher(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+
+ public ASN1Integer getxCoordinate()
+ {
+ return xCoordinate;
+ }
+
+ public void setxCoordinate(ASN1Integer xCoordinate)
+ {
+ this.xCoordinate = xCoordinate;
+ }
+
+ public ASN1Integer getyCoordinate()
+ {
+ return yCoordinate;
+ }
+
+ public void setyCoordinate(ASN1Integer yCoordinate)
+ {
+ this.yCoordinate = yCoordinate;
+ }
+
+ public ASN1OctetString getHash()
+ {
+ return hash;
+ }
+
+ public void setHash(ASN1OctetString hash)
+ {
+ this.hash = hash;
+ }
+
+ public ASN1OctetString getCipherText()
+ {
+ return cipherText;
+ }
+
+ public void setCipherText(ASN1OctetString cipherText)
+ {
+ this.cipherText = cipherText;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector(4);
+ v.add(xCoordinate);
+ v.add(yCoordinate);
+ v.add(hash);
+ v.add(cipherText);
+ return new DERSequence(v);
+ }
+
+ /**
+ * Convert ASN.1 Struct to C1C3C2 format
+ *
+ * @return C1C3C2
+ * @throws IOException
+ */
+ public byte[] convertC1C3C2() throws IOException
+ {
+ /*
+ * construct GMT0009-2012 encrypted data struct
+ */
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+
+ final byte[] x = new byte[32];
+ final byte[] y = new byte[32];
+
+ byte[] tmp = BigIntegers.asUnsignedByteArray(getxCoordinate().getValue());
+ System.arraycopy(tmp, 0, x, 32 - tmp.length, tmp.length);
+ tmp = BigIntegers.asUnsignedByteArray(getyCoordinate().getValue());
+ System.arraycopy(tmp, 0, y, 32 - tmp.length, tmp.length);
+
+ // C1
+ // read 1 byte for uncompressed point prefix 0x04
+ stream.write(0x04);
+ stream.write(x);
+ stream.write(y);
+ // C3
+ stream.write(getHash().getOctets());
+ // C2
+ stream.write(getCipherText().getOctets());
+ stream.flush();
+ return stream.toByteArray();
+ }
+
+ /**
+ * Convert SM2 encrypted result format of c1c3c2 to ASN.1 SM2Cipher
+ *
+ * @param c1c3c2 encrypted result
+ * @return SM2Cipher
+ * @throws IOException
+ */
+ static public SM2Cipher fromC1C3C2(byte[] c1c3c2) throws IOException
+ {
+ /*
+ * construct GMT0009-2012 encrypted data struct
+ */
+ ByteArrayInputStream stream = new ByteArrayInputStream(c1c3c2);
+ // read 1 byte for uncompressed point prefix 0x04
+ stream.read();
+ final byte[] x = new byte[32];
+ final byte[] y = new byte[32];
+ final byte[] hash = new byte[32];
+ int length = c1c3c2.length - 1 - 32 - 32 - 32;
+ final byte[] cipherText = new byte[length];
+ stream.read(x);
+ stream.read(y);
+ stream.read(hash);
+ stream.read(cipherText);
+
+ final SM2Cipher sm2Cipher = new SM2Cipher();
+ sm2Cipher.setxCoordinate(new ASN1Integer(new BigInteger(1, x)));
+ sm2Cipher.setyCoordinate(new ASN1Integer(new BigInteger(1, y)));
+ sm2Cipher.setHash(new DEROctetString(hash));
+ sm2Cipher.setCipherText(new DEROctetString(cipherText));
+ return sm2Cipher;
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/CipherSuiteInfo.java b/tls/src/main/java/org/bouncycastle/jsse/provider/CipherSuiteInfo.java
index 0e67d1db1e..06c50a3273 100644
--- a/tls/src/main/java/org/bouncycastle/jsse/provider/CipherSuiteInfo.java
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/CipherSuiteInfo.java
@@ -16,7 +16,7 @@ class CipherSuiteInfo
{
static CipherSuiteInfo forCipherSuite(int cipherSuite, String name)
{
- if (!name.startsWith("TLS_"))
+ if (!name.startsWith("TLS_") && !name.startsWith("GMSSL_"))
{
throw new IllegalArgumentException();
}
@@ -81,6 +81,14 @@ boolean isTLSv13()
return isTLSv13;
}
+ /**
+ * GMSSL 1.1 crypto suites Start with 0xe0
+ * @return true - GMSSL suite; false - not
+ */
+ boolean isGMSSLv11(){
+ return ((cipherSuite >> 8) & 0xFF) == 0xe0;
+ }
+
private static void addAll(Set
+ * - make handshake connection
+ * - no authentication
+ *
+ * @since 2021-03-09 14:01:50
+ */
+public class GMSimpleSSLClient extends AbstractTlsClient
+{
+
+
+ private static final int[] DEFAULT_CIPHER_SUITES = new int[]
+ {
+ /*
+ * GMSSL 1.1
+ */
+ CipherSuite.GMSSL_ECC_SM4_SM3,
+ };
+
+ public GMSimpleSSLClient()
+ {
+ this(new BcTlsCrypto(new SecureRandom()));
+ }
+
+ public GMSimpleSSLClient(TlsCrypto crypto)
+ {
+ super(crypto);
+ }
+
+ @Override
+ protected ProtocolVersion[] getSupportedVersions()
+ {
+ return new ProtocolVersion[]{ProtocolVersion.GMSSLv11};
+ }
+
+ protected int[] getSupportedCipherSuites()
+ {
+ return TlsUtils.getSupportedCipherSuites(getCrypto(), DEFAULT_CIPHER_SUITES);
+ }
+
+ public TlsAuthentication getAuthentication() throws IOException
+ {
+ return new TlsAuthentication()
+ {
+
+ public void notifyServerCertificate(TlsServerCertificate serverCertificate) throws IOException
+ {
+// System.out.println(">> TlsAuthentication on notifyServerCertificate");
+// System.out.println(serverCertificate.getCertificate());
+ }
+
+ public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException
+ {
+// System.out.println(">> TlsAuthentication on getClientCredentials");
+ return null;
+ }
+ };
+ }
+
+ /**
+ * GMSSL not support ClientExtensions
+ *
+ * @return empty list
+ * @throws IOException not happen
+ */
+ @Override
+ public Hashtable getClientExtensions() throws IOException
+ {
+ return new Hashtable(0);
+ }
+
+ /**
+ * GMSSL Client generate random struct should be
+ * struct
+ * {
+ * unit32 gmt_unix_time;
+ * opaque random_bytes[28];
+ * }
+ *
+ * @return true - use GMTUnixTime
+ */
+ @Override
+ public boolean shouldUseGMTUnixTime()
+ {
+ return true;
+ }
+
+ @Override
+ public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException
+ {
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLServer.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLServer.java
new file mode 100644
index 0000000000..a6f4782fff
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLServer.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.jsse.provider.gm;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.tls.*;
+import org.bouncycastle.tls.crypto.TlsCrypto;
+import org.bouncycastle.tls.crypto.impl.bc.BcGMSSLCredentials;
+import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.SecureRandom;
+
+/**
+ * Simple GMSSL Server
+ *
+ *
+ * @since 2021-03-16 09:31:14
+ */
+public class GMSimpleSSLServer
+ extends DefaultTlsServer
+{
+ /*
+ * contain two cert, first for sign, second for encrypt
+ */
+ protected Certificate certList;
+ protected AsymmetricKeyParameter signKey;
+ protected AsymmetricKeyParameter encKey;
+
+ /**
+ * Create GMSSL Server Instance
+ *
+ * @param crypto crypto
+ * @param certList contain two cert, first for sign, second for encrypt
+ * @param signKey sign private key
+ * @param encKey encrypt private key
+ */
+ public GMSimpleSSLServer(TlsCrypto crypto, Certificate certList, AsymmetricKeyParameter signKey, AsymmetricKeyParameter encKey)
+ {
+ super(crypto);
+ this.certList = certList;
+ this.signKey = signKey;
+ this.encKey = encKey;
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+// PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+// out.println("GMSSL server raised alert: " + AlertLevel.getText(alertLevel)
+// + ", " + AlertDescription.getText(alertDescription));
+// if (message != null)
+// {
+// out.println("> " + message);
+// }
+// if (cause != null)
+// {
+// cause.printStackTrace(out);
+// }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+// PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+// out.println("GMSSL server received alert: " + AlertLevel.getText(alertLevel)
+// + ", " + AlertDescription.getText(alertDescription));
+ }
+
+ @Override
+ public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException
+ {
+
+ }
+
+ public ProtocolVersion getServerVersion() throws IOException
+ {
+ return ProtocolVersion.GMSSLv11;
+ }
+
+ public CertificateRequest getCertificateRequest() throws IOException
+ {
+ return null;
+ }
+
+ public void notifyClientCertificate(Certificate clientCertificate) throws IOException
+ {
+
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+
+ }
+
+ @Override
+ public TlsCredentials getCredentials() throws IOException
+ {
+ return new BcGMSSLCredentials((BcTlsCrypto) getCrypto(), certList, signKey, encKey);
+ }
+
+ @Override
+ public boolean shouldUseGMTUnixTime()
+ {
+ return true;
+ }
+
+ protected int[] getSupportedCipherSuites()
+ {
+ return new int[]{CipherSuite.GMSSL_ECC_SM4_SM3};
+ }
+
+ protected ProtocolVersion[] getSupportedVersions()
+ {
+ return ProtocolVersion.GMSSLv11.only();
+ }
+
+ protected String hex(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.toHexString(data);
+ }
+
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocket.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocket.java
new file mode 100644
index 0000000000..cd2bac2d63
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocket.java
@@ -0,0 +1,295 @@
+package org.bouncycastle.jsse.provider.gm;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.tls.*;
+import org.bouncycastle.tls.crypto.TlsCrypto;
+
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketAddress;
+
+/**
+ * GMSSL Socket simple implement.
+ *
+ *
+ * @since 2021-03-16 13:18:06
+ */
+public class GMSimpleSSLSocket extends SSLSocket
+{
+ public static final String[] SUPPORT_CRYPTO_SUITES = {
+ "GMSSL_SM2_SM4_SM3"
+ };
+
+ public static final String[] SUPPORT_PROTOCOL_VERSIONS = {
+ "GMSSLv1.1"
+ };
+
+ protected boolean useClientMode = true;
+ private TlsProtocol protocol;
+
+ protected TlsCrypto crypto;
+ protected Certificate certList;
+ protected AsymmetricKeyParameter signKey, encKey;
+
+ private GMSession session;
+
+
+ protected GMSimpleSSLSocket()
+ {
+ super();
+ }
+
+ public GMSimpleSSLSocket(String host, int port) throws IOException
+ {
+ super(host, port);
+ }
+
+ public GMSimpleSSLSocket(InetAddress host, int port) throws IOException
+ {
+ super(host, port);
+ }
+
+ public GMSimpleSSLSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException
+ {
+ super(host, port, localAddress, localPort);
+ }
+
+ public GMSimpleSSLSocket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException
+ {
+ super(host, port, localAddress, localPort);
+ }
+
+ /**
+ * set socket use client mode
+ *
+ * @param crypto crypto
+ * @return GMSimpleSSLSocket
+ */
+ public GMSimpleSSLSocket useClientMode(TlsCrypto crypto)
+ {
+ this.useClientMode = true;
+ this.crypto = crypto;
+ this.certList = null;
+ this.signKey = null;
+ this.encKey = null;
+ return this;
+ }
+
+ /**
+ * set socket use server mode
+ *
+ * @param crypto crypto
+ * @param certList sign cert and encrypt cert
+ * @param signKey sign keypair
+ * @param encKey enc keypair
+ * @return GMSimpleSSLSocket
+ */
+ public GMSimpleSSLSocket useServerMode(TlsCrypto crypto, Certificate certList, AsymmetricKeyParameter signKey, AsymmetricKeyParameter encKey)
+ {
+ this.useClientMode = false;
+ this.crypto = crypto;
+ this.certList = certList;
+ this.signKey = signKey;
+ this.encKey = encKey;
+ return this;
+ }
+
+
+ @Override
+ public String[] getSupportedCipherSuites()
+ {
+ return SUPPORT_CRYPTO_SUITES;
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites()
+ {
+ return SUPPORT_CRYPTO_SUITES;
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] strings)
+ {
+
+ }
+
+ @Override
+ public String[] getSupportedProtocols()
+ {
+ return SUPPORT_PROTOCOL_VERSIONS;
+ }
+
+ @Override
+ public String[] getEnabledProtocols()
+ {
+ return SUPPORT_PROTOCOL_VERSIONS;
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] strings)
+ {
+
+ }
+
+ @Override
+ public void connect(SocketAddress endpoint) throws IOException
+ {
+ super.connect(endpoint);
+ }
+
+ @Override
+ public SSLSession getSession()
+ {
+ // prevent apache HttpClient get session null throw error
+ return session;
+ }
+
+ @Override
+ public void addHandshakeCompletedListener(HandshakeCompletedListener handshakeCompletedListener)
+ {
+
+ }
+
+ @Override
+ public void removeHandshakeCompletedListener(HandshakeCompletedListener handshakeCompletedListener)
+ {
+
+ }
+
+ @Override
+ public void startHandshake() throws IOException
+ {
+ InputStream input = super.getInputStream();
+ OutputStream output = super.getOutputStream();
+
+ makeHandshake(input, output);
+ }
+
+ protected void makeHandshake(InputStream input, OutputStream output) throws IOException
+ {
+ if(session == null)
+ {
+ session = new GMSession(this);
+ }
+ if(this.useClientMode)
+ {
+ GmSimpleTlsClientProtocol clientProtocol = new GmSimpleTlsClientProtocol(input, output);
+ this.protocol = clientProtocol;
+ session.renew(clientProtocol);
+ GMSimpleSSLClient client = new GMSimpleSSLClient(crypto);
+
+ clientProtocol.connect(client);
+ }
+ else
+ {
+ GMSimpleSSLServer server = new GMSimpleSSLServer(crypto, certList, signKey, encKey);
+ GmSimpleTlsServerProtocol serverProtocol = new GmSimpleTlsServerProtocol(input, output);
+ session.renew(serverProtocol);
+ this.protocol = serverProtocol;
+ serverProtocol.accept(server);
+ }
+
+ }
+
+ synchronized void handshakeIfNecessary() throws IOException
+ {
+
+ if (protocol == null || protocol.isHandshaking())
+ {
+ startHandshake();
+ }
+ }
+
+
+ @Override
+ public InputStream getInputStream() throws IOException
+ {
+ handshakeIfNecessary();
+ return this.protocol.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException
+ {
+ handshakeIfNecessary();
+ return this.protocol.getOutputStream();
+ }
+
+ @Override
+ public void setUseClientMode(boolean b)
+ {
+ this.useClientMode = b;
+ }
+
+ @Override
+ public boolean getUseClientMode()
+ {
+ return useClientMode;
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean b)
+ {
+ }
+
+ @Override
+ public boolean getNeedClientAuth()
+ {
+ return false;
+ }
+
+ @Override
+ public void setWantClientAuth(boolean b)
+ {
+
+ }
+
+ @Override
+ public boolean getWantClientAuth()
+ {
+ return false;
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean b)
+ {
+
+ }
+
+ @Override
+ public boolean getEnableSessionCreation()
+ {
+ return false;
+ }
+
+ @Override
+ public synchronized void close() throws IOException
+ {
+ if(protocol != null)
+ {
+ protocol.close();
+ }
+ }
+
+
+
+ @Override
+ protected void finalize() throws Throwable
+ {
+ try
+ {
+ close();
+ } catch (IOException e)
+ {
+ // Ignore
+ } finally
+ {
+ super.finalize();
+ }
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocketFactory.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocketFactory.java
new file mode 100644
index 0000000000..ffd910d3a0
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocketFactory.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.jsse.provider.gm;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.jcajce.provider.asymmetric.GM;
+import org.bouncycastle.tls.Certificate;
+import org.bouncycastle.tls.crypto.TlsCrypto;
+import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto;
+
+import javax.net.ssl.SSLSocketFactory;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+
+/**
+ * Simple GMSSL Socket Factory
+ *
+ *
+ * @since 2021-03-16 13:15:00
+ */
+public class GMSimpleSSLSocketFactory extends SSLSocketFactory
+{
+
+ private TlsCrypto crypto;
+ private Certificate certList;
+ private AsymmetricKeyParameter signKey, encKey;
+ private Boolean clientMode = true;
+
+
+ /**
+ * Create Client side socket factory Instance
+ */
+ public GMSimpleSSLSocketFactory()
+ {
+ this.crypto = new BcTlsCrypto(new SecureRandom());
+ }
+
+ /**
+ * Create server side socket factory Instance
+ *
+ * @param certList sign cert and encrypt cert
+ * @param signKey sign key
+ * @param encKey enc key
+ */
+ public GMSimpleSSLSocketFactory(Certificate certList, AsymmetricKeyParameter signKey, AsymmetricKeyParameter encKey)
+ {
+ this();
+ this.certList = certList;
+ this.signKey = signKey;
+ this.encKey = encKey;
+ clientMode = false;
+ }
+
+ /**
+ * Create Client side socket factory Instance
+ * @return factory Instance
+ */
+ public static GMSimpleSSLSocketFactory ClientFactory()
+ {
+ return new GMSimpleSSLSocketFactory();
+ }
+
+ /**
+ * Create server side socket factory Instance
+ *
+ * @param certList sign cert and encrypt cert
+ * @param signKey sign key
+ * @param encKey enc key
+ */
+ public static GMSimpleSSLSocketFactory ServerFactory(Certificate certList, AsymmetricKeyParameter signKey,
+ AsymmetricKeyParameter encKey)
+ {
+ return new GMSimpleSSLSocketFactory(certList, signKey,encKey);
+ }
+
+ @Override
+ public String[] getDefaultCipherSuites()
+ {
+ return GMSimpleSSLSocket.SUPPORT_CRYPTO_SUITES;
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites()
+ {
+ return GMSimpleSSLSocket.SUPPORT_CRYPTO_SUITES;
+ }
+
+ private GMSimpleSSLSocket ret(GMSimpleSSLSocket socket)
+ {
+ if(clientMode)
+ {
+ return socket.useClientMode(crypto);
+ }
+ else
+ {
+ return socket.useServerMode(crypto, certList, signKey, encKey);
+ }
+ }
+
+ @Override
+ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException
+ {
+ final GMSimpleSSLSocketWrap wrap = new GMSimpleSSLSocketWrap(socket, autoClose);
+ return ret(wrap);
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException
+ {
+ final GMSimpleSSLSocket socket = new GMSimpleSSLSocket(host, port);
+ return ret(socket);
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress inetAddress, int localPort) throws IOException, UnknownHostException
+ {
+ final GMSimpleSSLSocket socket = new GMSimpleSSLSocket(host, port, inetAddress, localPort);
+ return ret(socket);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress inetAddress, int port) throws IOException
+ {
+ final GMSimpleSSLSocket socket = new GMSimpleSSLSocket(inetAddress, port);
+ return ret(socket);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress inetAddress, int port, InetAddress localAddress, int localPort) throws IOException
+ {
+ final GMSimpleSSLSocket socket = new GMSimpleSSLSocket(inetAddress, port, localAddress, localPort);
+ return ret(socket);
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocketWrap.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocketWrap.java
new file mode 100644
index 0000000000..3c64ae709d
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GMSimpleSSLSocketWrap.java
@@ -0,0 +1,283 @@
+package org.bouncycastle.jsse.provider.gm;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.tls.Certificate;
+import org.bouncycastle.tls.TlsClientProtocol;
+import org.bouncycastle.tls.TlsProtocol;
+import org.bouncycastle.tls.TlsServerProtocol;
+import org.bouncycastle.tls.crypto.TlsCrypto;
+
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SocketChannel;
+
+/**
+ * GMSSL Socket simple implement.
+ *
+ *
+ * @since 2021-03-16 13:18:06
+ */
+public class GMSimpleSSLSocketWrap extends GMSimpleSSLSocket
+{
+ protected Socket wrapScoket = null;
+ protected boolean autoClose;
+
+ public GMSimpleSSLSocketWrap(Socket wrapScoket, boolean autoClose)
+ {
+ super();
+ this.wrapScoket = wrapScoket;
+ this.autoClose = autoClose;
+ }
+
+ @Override
+ public void connect(SocketAddress endpoint) throws IOException
+ {
+ throw new SocketException("Wrapped socket should already be connected");
+ }
+
+ @Override
+ public void connect(SocketAddress endpoint, int timeout) throws IOException
+ {
+ throw new SocketException("Wrapped socket should already be connected");
+ }
+
+ @Override
+ public void bind(SocketAddress bindpoint) throws IOException
+ {
+ wrapScoket.bind(bindpoint);
+ }
+
+ @Override
+ public InetAddress getInetAddress()
+ {
+ return wrapScoket.getInetAddress();
+ }
+
+ @Override
+ public InetAddress getLocalAddress()
+ {
+ return wrapScoket.getLocalAddress();
+ }
+
+ @Override
+ public int getPort()
+ {
+ return wrapScoket.getPort();
+ }
+
+ @Override
+ public int getLocalPort()
+ {
+ return wrapScoket.getLocalPort();
+ }
+
+ @Override
+ public SocketAddress getRemoteSocketAddress()
+ {
+ return wrapScoket.getRemoteSocketAddress();
+ }
+
+ @Override
+ public SocketAddress getLocalSocketAddress()
+ {
+ return wrapScoket.getLocalSocketAddress();
+ }
+
+ @Override
+ public SocketChannel getChannel()
+ {
+ return wrapScoket.getChannel();
+ }
+
+ @Override
+ public void setTcpNoDelay(boolean on) throws SocketException
+ {
+ wrapScoket.setTcpNoDelay(on);
+ }
+
+ @Override
+ public boolean getTcpNoDelay() throws SocketException
+ {
+ return wrapScoket.getTcpNoDelay();
+ }
+
+ @Override
+ public void setSoLinger(boolean on, int linger) throws SocketException
+ {
+ wrapScoket.setSoLinger(on, linger);
+ }
+
+ @Override
+ public int getSoLinger() throws SocketException
+ {
+ return wrapScoket.getSoLinger();
+ }
+
+ @Override
+ public void sendUrgentData(int data) throws IOException
+ {
+ wrapScoket.sendUrgentData(data);
+ }
+
+ @Override
+ public void setOOBInline(boolean on) throws SocketException
+ {
+ wrapScoket.setOOBInline(on);
+ }
+
+ @Override
+ public boolean getOOBInline() throws SocketException
+ {
+ return wrapScoket.getOOBInline();
+ }
+
+ @Override
+ public synchronized void setSoTimeout(int timeout) throws SocketException
+ {
+ wrapScoket.setSoTimeout(timeout);
+ }
+
+ @Override
+ public synchronized int getSoTimeout() throws SocketException
+ {
+ return wrapScoket.getSoTimeout();
+ }
+
+ @Override
+ public synchronized void setSendBufferSize(int size) throws SocketException
+ {
+ wrapScoket.setSendBufferSize(size);
+ }
+
+ @Override
+ public synchronized int getSendBufferSize() throws SocketException
+ {
+ return wrapScoket.getSendBufferSize();
+ }
+
+ @Override
+ public synchronized void setReceiveBufferSize(int size) throws SocketException
+ {
+ wrapScoket.setReceiveBufferSize(size);
+ }
+
+ @Override
+ public synchronized int getReceiveBufferSize() throws SocketException
+ {
+ return wrapScoket.getReceiveBufferSize();
+ }
+
+ @Override
+ public void setKeepAlive(boolean on) throws SocketException
+ {
+ wrapScoket.setKeepAlive(on);
+ }
+
+ @Override
+ public boolean getKeepAlive() throws SocketException
+ {
+ return wrapScoket.getKeepAlive();
+ }
+
+ @Override
+ public void setTrafficClass(int tc) throws SocketException
+ {
+ wrapScoket.setTrafficClass(tc);
+ }
+
+ @Override
+ public int getTrafficClass() throws SocketException
+ {
+ return wrapScoket.getTrafficClass();
+ }
+
+ @Override
+ public void setReuseAddress(boolean on) throws SocketException
+ {
+ wrapScoket.setReuseAddress(on);
+ }
+
+ @Override
+ public boolean getReuseAddress() throws SocketException
+ {
+ return wrapScoket.getReuseAddress();
+ }
+
+ @Override
+ public void shutdownInput() throws IOException
+ {
+ wrapScoket.shutdownInput();
+ }
+
+ @Override
+ public void shutdownOutput() throws IOException
+ {
+ wrapScoket.shutdownOutput();
+ }
+
+ @Override
+ public String toString()
+ {
+ return wrapScoket.toString();
+ }
+
+ @Override
+ public boolean isConnected()
+ {
+ return wrapScoket.isConnected();
+ }
+
+ @Override
+ public boolean isBound()
+ {
+ return wrapScoket.isBound();
+ }
+
+ @Override
+ public boolean isClosed()
+ {
+ return wrapScoket.isClosed();
+ }
+
+ @Override
+ public boolean isInputShutdown()
+ {
+ return wrapScoket.isInputShutdown();
+ }
+
+ @Override
+ public boolean isOutputShutdown()
+ {
+ return wrapScoket.isOutputShutdown();
+ }
+
+ @Override
+ public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
+ {
+ wrapScoket.setPerformancePreferences(connectionTime, latency, bandwidth);
+ }
+
+ @Override
+ public void startHandshake() throws IOException
+ {
+ makeHandshake(wrapScoket.getInputStream(), wrapScoket.getOutputStream());
+ }
+ @Override
+ public synchronized void close() throws IOException
+ {
+ super.close();
+ if(autoClose)
+ {
+ wrapScoket.close();
+ }
+ }
+
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GmSimpleTlsClientProtocol.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GmSimpleTlsClientProtocol.java
new file mode 100644
index 0000000000..b17ba2bf5b
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GmSimpleTlsClientProtocol.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.jsse.provider.gm;
+
+
+import org.bouncycastle.tls.SecurityParameters;
+import org.bouncycastle.tls.TlsClientProtocol;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class GmSimpleTlsClientProtocol extends TlsClientProtocol implements SecurityParameterProvider
+{
+ public GmSimpleTlsClientProtocol()
+ {
+ }
+
+ public GmSimpleTlsClientProtocol(InputStream input, OutputStream output)
+ {
+ super(input, output);
+ }
+
+ public SecurityParameters getSecurityParameters()
+ {
+ return super.getContext().getSecurityParameters();
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GmSimpleTlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GmSimpleTlsServerProtocol.java
new file mode 100644
index 0000000000..fc072669d9
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/GmSimpleTlsServerProtocol.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.jsse.provider.gm;
+
+
+import org.bouncycastle.tls.SecurityParameters;
+import org.bouncycastle.tls.TlsServerProtocol;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class GmSimpleTlsServerProtocol extends TlsServerProtocol implements SecurityParameterProvider
+{
+ public GmSimpleTlsServerProtocol()
+ {
+ }
+
+ public GmSimpleTlsServerProtocol(InputStream input, OutputStream output)
+ {
+ super(input, output);
+ }
+
+ public SecurityParameters getSecurityParameters()
+ {
+ return super.getContext().getSecurityParameters();
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/gm/SecurityParameterProvider.java b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/SecurityParameterProvider.java
new file mode 100644
index 0000000000..31cb351e16
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/jsse/provider/gm/SecurityParameterProvider.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.jsse.provider.gm;
+
+import org.bouncycastle.tls.SecurityParameters;
+
+
+public interface SecurityParameterProvider {
+
+ SecurityParameters getSecurityParameters();
+}
diff --git a/tls/src/main/java/org/bouncycastle/tls/AbstractTlsKeyExchangeFactory.java b/tls/src/main/java/org/bouncycastle/tls/AbstractTlsKeyExchangeFactory.java
index 4c84a9a06b..d2991bee0a 100644
--- a/tls/src/main/java/org/bouncycastle/tls/AbstractTlsKeyExchangeFactory.java
+++ b/tls/src/main/java/org/bouncycastle/tls/AbstractTlsKeyExchangeFactory.java
@@ -91,4 +91,9 @@ public TlsKeyExchange createSRPKeyExchangeServer(int keyExchange, TlsSRPLoginPar
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
+
+ public TlsKeyExchange createSM2KeyExchange(int keyExchange) throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/CipherSuite.java b/tls/src/main/java/org/bouncycastle/tls/CipherSuite.java
index 2d8ec979e5..f16e7316c7 100644
--- a/tls/src/main/java/org/bouncycastle/tls/CipherSuite.java
+++ b/tls/src/main/java/org/bouncycastle/tls/CipherSuite.java
@@ -444,4 +444,11 @@ public static boolean isSCSV(int cipherSuite)
public static final int TLS_CHACHA20_POLY1305_SHA256 = 0x1303;
public static final int TLS_AES_128_CCM_SHA256 = 0x1304;
public static final int TLS_AES_128_CCM_8_SHA256 = 0x1305;
+
+ /*
+ * GMT 0024-2014
+ */
+ public static final int GMSSL_ECC_SM4_SM3 = 0xe013;
+
+
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/ClientCertificateType.java b/tls/src/main/java/org/bouncycastle/tls/ClientCertificateType.java
index bfff66402b..4c1ed40f4a 100644
--- a/tls/src/main/java/org/bouncycastle/tls/ClientCertificateType.java
+++ b/tls/src/main/java/org/bouncycastle/tls/ClientCertificateType.java
@@ -19,4 +19,13 @@ public class ClientCertificateType
public static final short ecdsa_sign = 64;
public static final short rsa_fixed_ecdh = 65;
public static final short ecdsa_fixed_ecdh = 66;
+
+ /*
+ * GMT0024 has not mentioned
+ *
+ * Just specify a number here
+ */
+ public static final short sm2_encrypt = 128;
+
+
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/DefaultTlsKeyExchangeFactory.java b/tls/src/main/java/org/bouncycastle/tls/DefaultTlsKeyExchangeFactory.java
index 949860cd1f..2ff10a2d85 100644
--- a/tls/src/main/java/org/bouncycastle/tls/DefaultTlsKeyExchangeFactory.java
+++ b/tls/src/main/java/org/bouncycastle/tls/DefaultTlsKeyExchangeFactory.java
@@ -89,4 +89,17 @@ public TlsKeyExchange createSRPKeyExchangeServer(int keyExchange, TlsSRPLoginPar
{
return new TlsSRPKeyExchange(keyExchange, loginParameters);
}
+
+ /**
+ * GMSSL ECC_SM4_SM3 suite key exchange
+ *
+ * @param keyExchange enum type
+ * @return SM2 key exchange object
+ * @throws IOException err
+ */
+ @Override
+ public TlsKeyExchange createSM2KeyExchange(int keyExchange) throws IOException
+ {
+ return new TlsSM2KeyExchange(keyExchange);
+ }
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/EncryptionAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/EncryptionAlgorithm.java
index 847dba5ecb..73f8aebea5 100644
--- a/tls/src/main/java/org/bouncycastle/tls/EncryptionAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/EncryptionAlgorithm.java
@@ -66,4 +66,9 @@ public class EncryptionAlgorithm
* RFC 7905
*/
public static final int CHACHA20_POLY1305 = 21;
+
+ /*
+ * GMT 0024-2014
+ */
+ public static final int SM4_CBC = 31;
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/HashAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/HashAlgorithm.java
index 75332c0ed2..2ad2fea7df 100644
--- a/tls/src/main/java/org/bouncycastle/tls/HashAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/HashAlgorithm.java
@@ -18,6 +18,12 @@ public class HashAlgorithm
*/
public static final short Intrinsic = 8;
+ /*
+ * GMT 0024-2014 No value is specified,
+ * so a value is randomly specified here to avoid conflicts
+ */
+ public static final short sm3 = 20;
+
public static String getName(short hashAlgorithm)
{
switch (hashAlgorithm)
@@ -38,6 +44,8 @@ public static String getName(short hashAlgorithm)
return "sha512";
case Intrinsic:
return "Intrinsic";
+ case sm3:
+ return "sm3";
default:
return "UNKNOWN";
}
@@ -54,6 +62,7 @@ public static int getOutputSize(short hashAlgorithm)
case sha224:
return 28;
case sha256:
+ case sm3:
return 32;
case sha384:
return 48;
@@ -79,6 +88,7 @@ public static boolean isRecognized(short hashAlgorithm)
switch (hashAlgorithm)
{
case md5:
+ case sm3:
case sha1:
case sha224:
case sha256:
diff --git a/tls/src/main/java/org/bouncycastle/tls/KeyExchangeAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/KeyExchangeAlgorithm.java
index bc58335ded..c285e7cfe8 100644
--- a/tls/src/main/java/org/bouncycastle/tls/KeyExchangeAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/KeyExchangeAlgorithm.java
@@ -53,4 +53,9 @@ public class KeyExchangeAlgorithm
* RFC 5489
*/
public static final int ECDHE_PSK = 24;
+
+ /*
+ * GMT 0024-2014
+ */
+ public static final int SM2 = 50;
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/MACAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/MACAlgorithm.java
index 5b556bbd5b..d1588740d5 100644
--- a/tls/src/main/java/org/bouncycastle/tls/MACAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/MACAlgorithm.java
@@ -21,6 +21,12 @@ public class MACAlgorithm
public static final int hmac_sha384 = 4;
public static final int hmac_sha512 = 5;
+ /*
+ * GMT 0024-2014 No value is specified,
+ * so a value is randomly specified here to avoid conflicts
+ */
+ public static final int hmac_sm3 = 20;
+
public static String getName(int macAlgorithm)
{
switch (macAlgorithm)
@@ -37,6 +43,8 @@ public static String getName(int macAlgorithm)
return "hmac_sha384";
case hmac_sha512:
return "hmac_sha512";
+ case hmac_sm3:
+ return "hmac_sm3";
default:
return "UNKNOWN";
}
@@ -56,6 +64,7 @@ public static boolean isHMAC(int macAlgorithm)
case hmac_sha256:
case hmac_sha384:
case hmac_sha512:
+ case hmac_sm3:
return true;
default:
return false;
diff --git a/tls/src/main/java/org/bouncycastle/tls/PRFAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/PRFAlgorithm.java
index e42bb83a71..58075861fc 100644
--- a/tls/src/main/java/org/bouncycastle/tls/PRFAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/PRFAlgorithm.java
@@ -14,6 +14,7 @@ public class PRFAlgorithm
public static final int tls_prf_sha384 = 3;
public static final int tls13_hkdf_sha256 = 4;
public static final int tls13_hkdf_sha384 = 5;
+ public static final int gmssl11_prf_sm3 = 6;
public static String getName(int prfAlgorithm)
{
@@ -31,6 +32,8 @@ public static String getName(int prfAlgorithm)
return "tls13_hkdf_sha256";
case tls13_hkdf_sha384:
return "tls13_hkdf_sha384";
+ case gmssl11_prf_sm3:
+ return "gmssl11_prf_sm3";
default:
return "UNKNOWN";
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/ProtocolVersion.java b/tls/src/main/java/org/bouncycastle/tls/ProtocolVersion.java
index 2a4ab5c43f..b9510bc4f0 100644
--- a/tls/src/main/java/org/bouncycastle/tls/ProtocolVersion.java
+++ b/tls/src/main/java/org/bouncycastle/tls/ProtocolVersion.java
@@ -14,6 +14,9 @@ public final class ProtocolVersion
public static final ProtocolVersion DTLSv10 = new ProtocolVersion(0xFEFF, "DTLS 1.0");
public static final ProtocolVersion DTLSv12 = new ProtocolVersion(0xFEFD, "DTLS 1.2");
+ public static final ProtocolVersion GMSSLv11 = new ProtocolVersion(0x0101, "GMSSL 1.1");
+
+ static final ProtocolVersion CLIENT_GM_SUPPORTED_TLS = GMSSLv11;
static final ProtocolVersion CLIENT_EARLIEST_SUPPORTED_DTLS = DTLSv10;
static final ProtocolVersion CLIENT_EARLIEST_SUPPORTED_TLS = SSLv3;
static final ProtocolVersion CLIENT_LATEST_SUPPORTED_DTLS = DTLSv12;
@@ -142,6 +145,9 @@ static boolean isSupportedTLSVersionClient(ProtocolVersion version)
int fullVersion = version.getFullVersion();
+ if(fullVersion == CLIENT_GM_SUPPORTED_TLS.getFullVersion()){
+ return true;
+ }
return fullVersion >= CLIENT_EARLIEST_SUPPORTED_TLS.getFullVersion()
&& fullVersion <= CLIENT_LATEST_SUPPORTED_TLS.getFullVersion();
}
@@ -155,6 +161,9 @@ static boolean isSupportedTLSVersionServer(ProtocolVersion version)
int fullVersion = version.getFullVersion();
+ if(fullVersion == CLIENT_GM_SUPPORTED_TLS.getFullVersion()){
+ return true;
+ }
return fullVersion >= SERVER_EARLIEST_SUPPORTED_TLS.getFullVersion()
&& fullVersion <= SERVER_LATEST_SUPPORTED_TLS.getFullVersion();
}
@@ -218,6 +227,10 @@ public boolean isDTLS()
return getMajorVersion() == 0xFE;
}
+ public boolean isGMSSL(){
+ return getMajorVersion() == 0x01;
+ }
+
public boolean isSSL()
{
return this == SSLv3;
@@ -225,7 +238,9 @@ public boolean isSSL()
public boolean isTLS()
{
- return getMajorVersion() == 0x03;
+ final int majorVersion = getMajorVersion();
+ // GMSSL is also a variants TLS
+ return majorVersion == 0x03 || majorVersion == 0x01;
}
public ProtocolVersion getEquivalentTLSVersion()
@@ -240,6 +255,7 @@ public ProtocolVersion getEquivalentTLSVersion()
case 0xFD: return TLSv12;
default: return null;
}
+ case 0x01: return GMSSLv11;
default: return null;
}
}
@@ -347,6 +363,15 @@ public static ProtocolVersion get(int major, int minor)
{
switch (major)
{
+ case 0x01:
+ {
+ switch (minor)
+ {
+ case 0x01:
+ return GMSSLv11;
+ }
+ return getUnknownVersion(major, minor, "GMSSL");
+ }
case 0x03:
{
switch (minor)
diff --git a/tls/src/main/java/org/bouncycastle/tls/SignatureAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/SignatureAlgorithm.java
index 9e8b7382be..561e28b3b5 100644
--- a/tls/src/main/java/org/bouncycastle/tls/SignatureAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/SignatureAlgorithm.java
@@ -10,6 +10,7 @@ public class SignatureAlgorithm
public static final short dsa = 2;
public static final short ecdsa = 3;
+
/*
* RFC 8422
*/
@@ -33,6 +34,13 @@ public class SignatureAlgorithm
public static final short ecdsa_brainpoolP384r1tls13_sha384 = 27;
public static final short ecdsa_brainpoolP512r1tls13_sha512 = 28;
+
+ /*
+ * GMSSL GMT 0009-2012
+ */
+ public static final short sm2 = 200;
+
+
public static short getClientCertificateType(short signatureAlgorithm)
{
switch (signatureAlgorithm)
@@ -52,6 +60,7 @@ public static short getClientCertificateType(short signatureAlgorithm)
case SignatureAlgorithm.ecdsa:
case SignatureAlgorithm.ed25519:
case SignatureAlgorithm.ed448:
+ case SignatureAlgorithm.sm2:
return ClientCertificateType.ecdsa_sign;
// NOTE: Only valid from TLS 1.3, where ClientCertificateType is not used
@@ -97,6 +106,8 @@ public static String getName(short signatureAlgorithm)
return "ecdsa_brainpoolP384r1tls13_sha384";
case ecdsa_brainpoolP512r1tls13_sha512:
return "ecdsa_brainpoolP512r1tls13_sha512";
+ case sm2:
+ return "sm2";
default:
return "UNKNOWN";
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/SignatureAndHashAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/SignatureAndHashAlgorithm.java
index c3227f7662..8cf08750d0 100644
--- a/tls/src/main/java/org/bouncycastle/tls/SignatureAndHashAlgorithm.java
+++ b/tls/src/main/java/org/bouncycastle/tls/SignatureAndHashAlgorithm.java
@@ -29,6 +29,7 @@ public class SignatureAndHashAlgorithm
SignatureAlgorithm.ecdsa_brainpoolP384r1tls13_sha384);
public static final SignatureAndHashAlgorithm ecdsa_brainpoolP512r1tls13_sha512 = createInstanceIntrinsic(
SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512);
+ public static final SignatureAndHashAlgorithm sm2 = createInstanceIntrinsic(SignatureAlgorithm.sm2);
public static SignatureAndHashAlgorithm getInstance(short hashAlgorithm, short signatureAlgorithm)
{
@@ -67,6 +68,8 @@ public static SignatureAndHashAlgorithm getInstanceIntrinsic(short signatureAlgo
return ecdsa_brainpoolP384r1tls13_sha384;
case SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512:
return ecdsa_brainpoolP512r1tls13_sha512;
+ case SignatureAlgorithm.sm2:
+ return sm2;
default:
return new SignatureAndHashAlgorithm(HashAlgorithm.Intrinsic, signatureAlgorithm);
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java
index 2b05fa5415..268b23105d 100644
--- a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java
+++ b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java
@@ -1612,6 +1612,10 @@ protected void sendClientHello()
// TODO[tls13] Prevent offering SSLv3 AND TLSv13?
recordStream.setWriteVersion(ProtocolVersion.SSLv3);
}
+ else if (ProtocolVersion.contains(tlsClientContext.getClientSupportedVersions(), ProtocolVersion.GMSSLv11))
+ {
+ recordStream.setWriteVersion(ProtocolVersion.GMSSLv11);
+ }
else
{
recordStream.setWriteVersion(ProtocolVersion.TLSv10);
diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsKeyExchangeFactory.java b/tls/src/main/java/org/bouncycastle/tls/TlsKeyExchangeFactory.java
index 81197f23ce..ae82c9fbd4 100644
--- a/tls/src/main/java/org/bouncycastle/tls/TlsKeyExchangeFactory.java
+++ b/tls/src/main/java/org/bouncycastle/tls/TlsKeyExchangeFactory.java
@@ -44,4 +44,14 @@ TlsKeyExchange createSRPKeyExchangeClient(int keyExchange, TlsSRPIdentity srpIde
TlsKeyExchange createSRPKeyExchangeServer(int keyExchange, TlsSRPLoginParameters loginParameters)
throws IOException;
+
+ /**
+ * GMSSL ECC_SM4_SM3 suite key exchange
+ *
+ * @param keyExchange enum type
+ * @return SM2 key exchange object
+ * @throws IOException err
+ */
+ TlsKeyExchange createSM2KeyExchange(int keyExchange)
+ throws IOException;
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsSM2KeyExchange.java b/tls/src/main/java/org/bouncycastle/tls/TlsSM2KeyExchange.java
new file mode 100644
index 0000000000..ba959707c0
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/tls/TlsSM2KeyExchange.java
@@ -0,0 +1,220 @@
+package org.bouncycastle.tls;
+
+import org.bouncycastle.tls.crypto.TlsCertificate;
+import org.bouncycastle.tls.crypto.TlsCryptoParameters;
+import org.bouncycastle.tls.crypto.TlsSecret;
+import org.bouncycastle.tls.crypto.TlsVerifier;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * GMSSL SM2 exchange.
+ *
+ *
+ */
+public class TlsSM2KeyExchange extends AbstractTlsKeyExchange
+{
+
+
+ private static int checkKeyExchange(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.SM2:
+ return keyExchange;
+ default:
+ throw new IllegalArgumentException("unsupported key exchange algorithm");
+ }
+ }
+
+ protected TlsCredentialedDecryptor serverDecryptor = null;
+ protected TlsCredentialedSigner serverSigner = null;
+ /**
+ * first cert of certificate list
+ * use to sign server side key exchange message
+ *
+ * digitally-signed struct
+ * {
+ * opaque client_random[32];
+ * opaque server_random[32];
+ * opaque ASN.1Cert<1..2^24-1>
+ * } signed params
+ */
+ protected TlsCertificate serverSigCertificate;
+
+ /**
+ * second cert of certificate list
+ * use to encrypt client generate preMasterSecret.
+ */
+ protected TlsCertificate serverEncCertificate;
+
+ protected TlsSecret preMasterSecret;
+
+ public TlsSM2KeyExchange(int keyExchange)
+ {
+ super(checkKeyExchange(keyExchange));
+ }
+
+ public void skipServerCredentials() throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public void processServerCredentials(TlsCredentials serverCredentials) throws IOException
+ {
+ if(serverCredentials instanceof TlsCredentialedDecryptor && serverCredentials instanceof TlsCredentialedSigner)
+ {
+ serverSigner = (TlsCredentialedSigner) serverCredentials;
+ serverDecryptor = (TlsCredentialedDecryptor) serverCredentials;
+ final TlsCertificate[] certificateList = serverCredentials.getCertificate().getCertificateList();
+ if(certificateList == null || certificateList.length < 2)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ // get certificate
+ serverSigCertificate = certificateList[0];
+ serverEncCertificate = certificateList[1];
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ @Override
+ public byte[] generateServerKeyExchange() throws IOException
+ {
+ // build key exchange message plaintext, struct see #buildServerKeyExchangeParams method.
+ final byte[] plaintext = buildServerKeyExchangeParams();
+ final byte[] signature = serverSigner.generateRawSignature(plaintext);
+ ByteArrayOutputStream bout = new ByteArrayOutputStream(signature.length+2);
+ TlsUtils.writeOpaque16(signature,bout);
+ return bout.toByteArray();
+ }
+
+ @Override
+ public void processServerKeyExchange(InputStream input) throws IOException
+ {
+
+ final int n = TlsUtils.readUint16(input);
+ final byte[] signature = TlsUtils.readFully(n, input);
+ // build KeyExchangeParams plaintext.
+ byte[] plaintext = buildServerKeyExchangeParams();
+ final TlsVerifier verifier = serverSigCertificate.createVerifier(SignatureAlgorithm.sm2);
+
+ DigitallySigned digitallySigned = new DigitallySigned(SignatureAndHashAlgorithm.sm2, signature);
+ final boolean pass = verifier.verifyRawSignature(digitallySigned, plaintext);
+ if(!pass)
+ {
+ throw new TlsFatalAlertReceived(AlertDescription.illegal_parameter);
+ }
+// this.serverSigCertificate.createVerifier()
+ return;
+ }
+
+ public void processServerCertificate(Certificate serverCertificate) throws IOException
+ {
+ // GMSSL has two certificates, first is for signing the second is for encryption
+ if(serverCertificate.getLength() < 2)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ // sign cert
+ this.serverSigCertificate = serverCertificate.getCertificateAt(0).useInRole(ConnectionEnd.server, KeyExchangeAlgorithm.SM2);
+ // encrypt cert
+ this.serverEncCertificate = serverCertificate.getCertificateAt(1).useInRole(ConnectionEnd.server, KeyExchangeAlgorithm.SM2);
+ }
+
+ public short[] getClientCertificateTypes()
+ {
+ return new short[]{ClientCertificateType.sm2_encrypt};
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials) throws IOException
+ {
+
+ }
+
+ /**
+ * generate preMasterSecret then use enc certificate public key
+ * enc preMasterSecret
+ *
+ * @param output
+ * @throws IOException
+ */
+ public void generateClientKeyExchange(OutputStream output) throws IOException
+ {
+ /*
+ * GMSSL PreMasterSecret struct same with rsaPreMasterSecret
+ *
+ * struct
+ * {
+ * ProtocolVersion client_version;
+ * opaque random[46];
+ * } PreMasterSecret
+ */
+ this.preMasterSecret = context.getCrypto().generateRSAPreMasterSecret(context.getClientVersion());
+ // add BcGmsslEncryptor to support encrypt preMasterSecret
+ byte[] encryptedPreMasterSecret = preMasterSecret.encrypt(serverEncCertificate);
+ TlsUtils.writeEncryptedPMS(context, encryptedPreMasterSecret, output);
+ }
+
+ public void processClientKeyExchange(InputStream input) throws IOException
+ {
+ byte[] encryptedPreMasterSecret = TlsUtils.readEncryptedPMS(context, input);
+ this.preMasterSecret = serverDecryptor.decrypt(new TlsCryptoParameters(context), encryptedPreMasterSecret);
+ }
+
+ public TlsSecret generatePreMasterSecret() throws IOException
+ {
+ TlsSecret tmp = this.preMasterSecret;
+ this.preMasterSecret = null;
+ return tmp;
+ }
+
+ /**
+ * build Server side Key Exchange Params plaintext.
+ * @return params plaintext
+ * @throws IOException
+ */
+ private byte[] buildServerKeyExchangeParams() throws IOException
+ {
+ /*
+ * SM2_SM4_SM3 suite ServerKeyExchange message struct
+ *
+ * GM0009-2012: SM2 is called ECC,
+ *
+ * enum {ECDHE,ECC,IBSDH,IBC,RSA} KeyExchangeAlgorithm;
+ * struct
+ * {
+ * select(KeyExchangeAlgorithm) {
+ * case ECC:
+ * digitally-signed struct
+ * {
+ * opaque client_random[32];
+ * opaque server_random[32];
+ * opaque ASN.1Cert<1..2^24-1>
+ * } signed_params
+ * }
+ * } ServerKeyExchange;
+ *
+ * the ASN.1Cert field is encrypt certificate
+ */
+ final SecurityParameters securityParameters = context.getSecurityParametersHandshake();
+ final byte[] clientRandom = securityParameters.getClientRandom();
+ final byte[] serverRandom = securityParameters.getServerRandom();
+ final byte[] encCert = serverEncCertificate.getEncoded();
+ int totalSize = clientRandom.length + serverRandom.length + 3 + encCert.length;
+ byte[] plaintext = new byte[totalSize];
+ System.arraycopy(clientRandom, 0, plaintext, 0, 32);
+ System.arraycopy(serverRandom, 0, plaintext, 32, 32);
+ plaintext[64] = (byte) (0xff & (encCert.length >> 16));
+ plaintext[65] = (byte) (0xff & (encCert.length >> 8));
+ plaintext[66] = (byte) (0xff & (encCert.length));
+ System.arraycopy(encCert, 0, plaintext, 67, encCert.length);
+ return plaintext;
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java
index b60f146ee6..f35ba06a47 100644
--- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java
+++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java
@@ -174,7 +174,7 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, boolean aft
/*
* TODO[tls13] Confirm fields in the ClientHello haven't changed
- *
+ *
* RFC 8446 4.1.2 [..] when the server has responded to its ClientHello with a
* HelloRetryRequest [..] the client MUST send the same ClientHello without
* modification, except as follows: [key_share, early_data, cookie, pre_shared_key,
@@ -223,7 +223,7 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, boolean aft
/*
* NOTE: Currently no server support for session resumption
- *
+ *
* If adding support, ensure securityParameters.tlsUnique is set to the localVerifyData, but
* ONLY when extended_master_secret has been negotiated (otherwise NULL).
*/
@@ -334,7 +334,7 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, boolean aft
/*
* TODO[tls13] RFC 8446 4.4.2.1. OCSP Status and SCT Extensions.
- *
+ *
* OCSP information is carried in an extension for a CertificateEntry.
*/
securityParameters.statusRequestVersion = clientHelloExtensions.containsKey(TlsExtensionsUtils.EXT_status_request)
@@ -344,7 +344,7 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, boolean aft
{
int namedGroup = clientShare.getNamedGroup();
-
+
TlsAgreement agreement;
if (NamedGroup.refersToASpecificCurve(namedGroup))
{
@@ -390,7 +390,6 @@ protected ServerHello generateServerHello(ClientHello clientHello) throws IOExce
this.offeredCipherSuites = clientHello.getCipherSuites();
-
SecurityParameters securityParameters = tlsServerContext.getSecurityParametersHandshake();
tlsServerContext.setClientSupportedVersions(
@@ -399,22 +398,37 @@ protected ServerHello generateServerHello(ClientHello clientHello) throws IOExce
ProtocolVersion clientVersion = clientLegacyVersion;
if (null == tlsServerContext.getClientSupportedVersions())
{
- if (clientVersion.isLaterVersionOf(ProtocolVersion.TLSv12))
+ if(clientVersion.getEquivalentTLSVersion() == ProtocolVersion.GMSSLv11)
{
- clientVersion = ProtocolVersion.TLSv12;
+ tlsServerContext.setClientSupportedVersions(new ProtocolVersion[]{clientVersion});
+ }
+ else
+ {
+ if (clientVersion.isLaterVersionOf(ProtocolVersion.TLSv12))
+ {
+ clientVersion = ProtocolVersion.TLSv12;
+ }
+
+ tlsServerContext.setClientSupportedVersions(clientVersion.downTo(ProtocolVersion.SSLv3));
}
- tlsServerContext.setClientSupportedVersions(clientVersion.downTo(ProtocolVersion.SSLv3));
}
else
{
clientVersion = ProtocolVersion.getLatestTLS(tlsServerContext.getClientSupportedVersions());
}
+ // check server client is support client versions
+ if(!ProtocolVersion.contains(tlsServer.getProtocolVersions(), clientVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+
// Set the legacy_record_version to use for early alerts
recordStream.setWriteVersion(clientVersion);
- if (!ProtocolVersion.SERVER_EARLIEST_SUPPORTED_TLS.isEqualOrEarlierVersionOf(clientVersion))
+ if (!ProtocolVersion.SERVER_EARLIEST_SUPPORTED_TLS.isEqualOrEarlierVersionOf(clientVersion)
+ && !ProtocolVersion.CLIENT_GM_SUPPORTED_TLS.isEqualOrEarlierVersionOf(clientVersion))
{
throw new TlsFatalAlert(AlertDescription.protocol_version);
}
@@ -721,7 +735,7 @@ protected void handle13HandshakeMessage(short type, HandshakeMessageInput buf)
{
/*
* TODO[tls13] Abbreviated handshakes (PSK resumption)
- *
+ *
* NOTE: No CertificateRequest, Certificate, CertificateVerify messages, but client
* might now send EndOfEarlyData after receiving server Finished message.
*/
@@ -1099,7 +1113,7 @@ else if (TlsUtils.isTLSv12(tlsServerContext))
/*
* RFC 5246 If no suitable certificate is available, the client MUST send a
* certificate message containing no certificates.
- *
+ *
* NOTE: In previous RFCs, this was SHOULD instead of MUST.
*/
throw new TlsFatalAlert(AlertDescription.unexpected_message);
diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java
index d71da52937..5c3dbdf398 100644
--- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java
+++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java
@@ -316,6 +316,10 @@ public static boolean isTLSv13(ProtocolVersion version)
return ProtocolVersion.TLSv13.isEqualOrEarlierVersionOf(version.getEquivalentTLSVersion());
}
+ public static boolean isGMSSLv11(ProtocolVersion version) {
+ return ProtocolVersion.GMSSLv11.isEqualOrEarlierVersionOf(version.getEquivalentTLSVersion());
+ }
+
public static boolean isTLSv13(TlsContext context)
{
return isTLSv13(context.getServerVersion());
@@ -1793,6 +1797,8 @@ public static short getHashAlgorithmForHMACAlgorithm(int macAlgorithm)
return HashAlgorithm.sha384;
case MACAlgorithm.hmac_sha512:
return HashAlgorithm.sha512;
+ case MACAlgorithm.hmac_sm3:
+ return HashAlgorithm.sm3;
default:
throw new IllegalArgumentException("specified MACAlgorithm not an HMAC: " + MACAlgorithm.getText(macAlgorithm));
}
@@ -1811,6 +1817,8 @@ public static short getHashAlgorithmForPRFAlgorithm(int prfAlgorithm)
case PRFAlgorithm.tls_prf_sha384:
case PRFAlgorithm.tls13_hkdf_sha384:
return HashAlgorithm.sha384;
+ case PRFAlgorithm.gmssl11_prf_sm3:
+ return HashAlgorithm.sm3;
default:
throw new IllegalArgumentException("unknown PRFAlgorithm: " + PRFAlgorithm.getText(prfAlgorithm));
}
@@ -1841,6 +1849,7 @@ static int getPRFAlgorithm(SecurityParameters securityParameters, int cipherSuit
{
ProtocolVersion negotiatedVersion = securityParameters.getNegotiatedVersion();
+ final boolean isGMSSLv11 = isGMSSLv11(negotiatedVersion);
final boolean isTLSv13 = isTLSv13(negotiatedVersion);
final boolean isTLSv12Exactly = !isTLSv13 && isTLSv12(negotiatedVersion);
final boolean isSSL = negotiatedVersion.isSSL();
@@ -2090,6 +2099,15 @@ static int getPRFAlgorithm(SecurityParameters securityParameters, int cipherSuit
return PRFAlgorithm.tls_prf_legacy;
}
+ case CipherSuite.GMSSL_ECC_SM4_SM3:
+ {
+ if (isGMSSLv11)
+ {
+ return PRFAlgorithm.gmssl11_prf_sm3;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
default:
{
if (isTLSv13)
@@ -2880,6 +2898,9 @@ public static int getEncryptionAlgorithm(int cipherSuite)
case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
return EncryptionAlgorithm.SEED_CBC;
+ case CipherSuite.GMSSL_ECC_SM4_SM3:
+ return EncryptionAlgorithm.SM4_CBC;
+
default:
return -1;
}
@@ -2914,6 +2935,7 @@ public static int getEncryptionAlgorithmType(int encryptionAlgorithm)
case EncryptionAlgorithm.CAMELLIA_128_CBC:
case EncryptionAlgorithm.CAMELLIA_256_CBC:
case EncryptionAlgorithm.SEED_CBC:
+ case EncryptionAlgorithm.SM4_CBC:
return CipherType.block;
case EncryptionAlgorithm.NULL:
@@ -3257,6 +3279,9 @@ public static int getKeyExchangeAlgorithm(int cipherSuite)
case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
return KeyExchangeAlgorithm.SRP_RSA;
+ case CipherSuite.GMSSL_ECC_SM4_SM3:
+ return KeyExchangeAlgorithm.SM2;
+
default:
return -1;
}
@@ -3577,6 +3602,9 @@ public static int getMACAlgorithm(int cipherSuite)
case CipherSuite.TLS_RSA_WITH_ARIA_256_CBC_SHA384:
return MACAlgorithm.hmac_sha384;
+ case CipherSuite.GMSSL_ECC_SM4_SM3:
+ return MACAlgorithm.hmac_sm3;
+
default:
return -1;
}
@@ -3773,6 +3801,9 @@ public static ProtocolVersion getMinimumVersion(int cipherSuite)
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
return ProtocolVersion.TLSv12;
+ case CipherSuite.GMSSL_ECC_SM4_SM3:
+ return ProtocolVersion.GMSSLv11;
+
default:
return ProtocolVersion.SSLv3;
}
@@ -4191,7 +4222,8 @@ public static boolean isSupportedKeyExchange(TlsCrypto crypto, int keyExchangeAl
case KeyExchangeAlgorithm.SRP_RSA:
return crypto.hasSRPAuthentication()
&& hasAnyRSASigAlgs(crypto);
-
+ case KeyExchangeAlgorithm.SM2:
+ return true;
default:
return false;
}
@@ -4269,6 +4301,9 @@ private static TlsKeyExchange createKeyExchangeClient(TlsClient client, int keyE
return factory.createSRPKeyExchangeClient(keyExchange, client.getSRPIdentity(),
client.getSRPConfigVerifier());
+ case KeyExchangeAlgorithm.SM2:
+ return factory.createSM2KeyExchange(keyExchange);
+
default:
/*
* Note: internal error here; the TlsProtocol implementation verifies that the
@@ -4326,6 +4361,9 @@ private static TlsKeyExchange createKeyExchangeServer(TlsServer server, int keyE
case KeyExchangeAlgorithm.SRP_RSA:
return factory.createSRPKeyExchangeServer(keyExchange, server.getSRPLoginParameters());
+ case KeyExchangeAlgorithm.SM2:
+ return factory.createSM2KeyExchange(keyExchange);
+
default:
/*
* Note: internal error here; the TlsProtocol implementation verifies that the
@@ -5205,7 +5243,7 @@ static TlsCredentials validateCredentials(TlsCredentials credentials) throws IOE
count += (credentials instanceof TlsCredentialedAgreement) ? 1 : 0;
count += (credentials instanceof TlsCredentialedDecryptor) ? 1 : 0;
count += (credentials instanceof TlsCredentialedSigner) ? 1 : 0;
- if (count != 1)
+ if (count < 1)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/BcGmsslEncryptor.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/BcGmsslEncryptor.java
new file mode 100644
index 0000000000..dffa954dd3
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/BcGmsslEncryptor.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.tls.crypto.impl;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.tls.AlertDescription;
+import org.bouncycastle.tls.TlsFatalAlert;
+import org.bouncycastle.tls.crypto.impl.bc.SM2Cipher;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+/**
+ * GMSSL basic chinese GMT 0009-2012
+ *
+ *
+ * @since 2021-03-10 13:56:20
+ */
+public class BcGmsslEncryptor implements TlsEncryptor
+{
+
+
+ private final ParametersWithRandom keyParameters;
+
+ public BcGmsslEncryptor(ECPublicKeyParameters keyParameters, SecureRandom secureRandom)
+ {
+ this.keyParameters = new ParametersWithRandom(keyParameters, secureRandom);
+
+ }
+
+ public byte[] encrypt(byte[] input, int inOff, int length) throws IOException
+ {
+ try
+ {
+ SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
+ engine.init(true, keyParameters);
+ byte[] c1c3c2 = engine.processBlock(input, inOff, length);
+ return SM2Cipher.fromC1C3C2(c1c3c2).getEncoded();
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/BcTlsRSAEncryptor.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/BcTlsRSAEncryptor.java
new file mode 100644
index 0000000000..072a88b601
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/BcTlsRSAEncryptor.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.tls.crypto.impl;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.tls.AlertDescription;
+import org.bouncycastle.tls.TlsFatalAlert;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+public class BcTlsRSAEncryptor
+ implements TlsEncryptor
+{
+
+ private final SecureRandom secureRandom;
+ private CipherParameters pubKeyRSA;
+
+ public BcTlsRSAEncryptor(RSAKeyParameters pubKeyRSA, SecureRandom secureRandom)
+ {
+ this.pubKeyRSA = pubKeyRSA;
+ this.secureRandom = secureRandom;
+
+ }
+
+ public byte[] encrypt(byte[] input, int inOff, int length)
+ throws IOException
+ {
+ try
+ {
+ PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine());
+ encoding.init(true, new ParametersWithRandom(pubKeyRSA, secureRandom));
+ return encoding.processBlock(input, inOff, length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ /*
+ * This should never happen, only during decryption.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
+ }
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/TlsBlockCipher.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/TlsBlockCipher.java
index 5000b2bb99..d700d2e745 100644
--- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/TlsBlockCipher.java
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/TlsBlockCipher.java
@@ -28,6 +28,7 @@ public class TlsBlockCipher
protected final boolean useExplicitIV;
protected final boolean acceptExtraPadding;
protected final boolean useExtraPadding;
+ protected final boolean useGMSSL;
protected final TlsBlockCipherImpl decryptCipher, encryptCipher;
protected final TlsSuiteMac readMac, writeMac;
@@ -49,6 +50,7 @@ public TlsBlockCipher(TlsCrypto crypto, TlsCryptoParameters cryptoParams, TlsBlo
this.encryptThenMAC = securityParameters.isEncryptThenMAC();
this.useExplicitIV = TlsImplUtils.isTLSv11(negotiatedVersion);
+ this.useGMSSL = TlsUtils.isGMSSLv11(negotiatedVersion);
this.acceptExtraPadding = !negotiatedVersion.isSSL();
@@ -205,7 +207,7 @@ public TlsEncodeResult encodePlaintext(long seqNo, short contentType, ProtocolVe
}
int totalSize = len + macSize + padding_length;
- if (useExplicitIV)
+ if (useExplicitIV || useGMSSL)
{
totalSize += blockSize;
}
@@ -221,6 +223,29 @@ public TlsEncodeResult encodePlaintext(long seqNo, short contentType, ProtocolVe
outOff += blockSize;
}
+ if (useGMSSL)
+ {
+ /*
+ * GMSSL GenericBlockCipher struct same as RFC5246 TLS 1.2
+ * struct {
+ * opaque IV[SecurityParameters.record_iv_length];
+ * block-ciphered struct {
+ * opaque content[TLSCompressed.length];
+ * opaque MAC[SecurityParameters.mac_length];
+ * uint8 padding[GenericBlockCipher.padding_length];
+ * uint8 padding_length;
+ * };
+ * } GenericBlockCipher;
+ */
+ byte[] explicitIV = cryptoParams.getNonceGenerator().generateNonce(blockSize);
+ System.arraycopy(explicitIV, 0, outBuf, outOff, blockSize);
+ // set cipher with new iv.
+ encryptCipher.init(explicitIV, 0, blockSize);
+ // GMSSL use explicit IV put after header, this part not be encrypted.
+ headerAllocation += blockSize;
+ outOff += blockSize;
+ }
+
System.arraycopy(plaintext, offset, outBuf, outOff, len);
outOff += len;
@@ -311,6 +336,18 @@ public TlsDecodeResult decodeCiphertext(long seqNo, short recordType, ProtocolVe
}
}
+ if(useGMSSL)
+ {
+ // Get explicit IV from begin of message
+ byte[] explicitIV = new byte[blockSize];
+ System.arraycopy(ciphertext, offset, explicitIV, 0, blockSize);
+ // set explicit IV to decrypt cipher
+ decryptCipher.init(explicitIV, 0, blockSize);
+ // encrypted part does not include iv
+ offset += blockSize;
+ blocks_length -= blockSize;
+ }
+
decryptCipher.doFinal(ciphertext, offset, blocks_length, ciphertext, offset);
if (useExplicitIV)
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcGMSSLCredentials.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcGMSSLCredentials.java
new file mode 100644
index 0000000000..f004ac9de0
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcGMSSLCredentials.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.tls.crypto.impl.bc;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.SM2Signer;
+import org.bouncycastle.tls.*;
+import org.bouncycastle.tls.crypto.TlsCryptoParameters;
+import org.bouncycastle.tls.crypto.TlsSecret;
+import org.bouncycastle.tls.crypto.TlsStreamSigner;
+
+import java.io.IOException;
+import java.security.Signature;
+
+/**
+ * GMSSL need two certificate
+ * one for sign, one for encrypt
+ * so we need provider Signer and Decryptor both
+ *
+ *
+ * @since 2021-03-12 12:10:31
+ */
+public class BcGMSSLCredentials implements TlsCredentialedSigner, TlsCredentialedDecryptor
+{
+ private BcTlsCrypto crypto;
+ /*
+ * first cert for sign, second sert for encrypt
+ */
+ private Certificate certList;
+
+ private AsymmetricKeyParameter signKey;
+ private AsymmetricKeyParameter encKey;
+ private Signature rawSigner;
+
+ public BcGMSSLCredentials(BcTlsCrypto crypto, Certificate certList, AsymmetricKeyParameter signKey, AsymmetricKeyParameter encKey)
+ {
+ if(certList.getLength() < 2)
+ {
+ throw new IllegalArgumentException("GMSSL need two certificate, first one for sign second one for encrypt.");
+ }
+
+ this.crypto = crypto;
+ this.certList = certList;
+ this.signKey = signKey;
+ this.encKey = encKey;
+ }
+
+ /**
+ * Use encrypt key decrypt ciphertext
+ *
+ * @param cryptoParams the parameters to use for the decryption.
+ * @param ciphertext the cipher text containing the secret.
+ * @return
+ * @throws IOException
+ */
+ public TlsSecret decrypt(TlsCryptoParameters cryptoParams, byte[] ciphertext) throws IOException
+ {
+ try
+ {
+ // Parser ciphertext as ASN.1 SM2Cipher object.
+ final SM2Cipher sm2Cipher = SM2Cipher.getInstance(ciphertext);
+ byte[] c1c3c2 = sm2Cipher.convertC1C3C2();
+ SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
+ engine.init(false, encKey);
+ byte[] preMasterSecret = engine.processBlock(c1c3c2, 0, c1c3c2.length);
+ return new BcTlsSecret(crypto, preMasterSecret);
+ } catch (Exception e)
+ {
+ throw new TlsFatalAlertReceived(AlertDescription.illegal_parameter);
+ }
+ }
+
+ /**
+ * Use sign private key sign
+ *
+ * @param hash a message digest calculated across the message the signature is to apply to.
+ * @return signature
+ * @throws IOException
+ */
+ public byte[] generateRawSignature(byte[] hash) throws IOException
+ {
+ try
+ {
+ final ParametersWithRandom prvKey = new ParametersWithRandom(signKey, crypto.getSecureRandom());
+ SM2Signer sm2Signer = new SM2Signer();
+ sm2Signer.init(true, prvKey);
+ sm2Signer.update(hash, 0, hash.length);
+ return sm2Signer.generateSignature();
+ }
+ catch (CryptoException e)
+ {
+ e.printStackTrace();
+ throw new TlsFatalAlertReceived(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+ {
+ return SignatureAndHashAlgorithm.sm2;
+ }
+
+
+ /**
+ * GMSSL not support Stream mode
+ *
+ * @return null
+ * @throws IOException not happen
+ */
+ public TlsStreamSigner getStreamSigner() throws IOException
+ {
+ return null;
+ }
+
+ public Certificate getCertificate()
+ {
+ return certList;
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCertificate.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCertificate.java
index 0798f8df22..99631cbc5a 100644
--- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCertificate.java
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCertificate.java
@@ -118,6 +118,9 @@ public TlsVerifier createVerifier(short signatureAlgorithm) throws IOException
validateRSA_PSS_PSS(signatureAlgorithm);
return new BcTlsRSAPSSVerifier(crypto, getPubKeyRSA(), signatureAlgorithm);
+ case SignatureAlgorithm.sm2:
+ return new BcTlsSM2Verifier(crypto, getPubKeyEC());
+
default:
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
}
@@ -279,6 +282,24 @@ public RSAKeyParameters getPubKeyRSA() throws IOException
}
}
+ /**
+ * Get public key from sm2 certificate
+ * @return sm2 public key
+ * @throws IOException err
+ */
+ public ECPublicKeyParameters getPubKeySM2() throws IOException
+ {
+ try
+ {
+ return (ECPublicKeyParameters)getPublicKey();
+ }
+ catch (ClassCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+ }
+ }
+
+
public boolean supportsSignatureAlgorithm(short signatureAlgorithm) throws IOException
{
return supportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.digitalSignature);
@@ -321,6 +342,12 @@ public TlsCertificate useInRole(int connectionEnd, int keyExchangeAlgorithm) thr
this.pubKeyRSA = getPubKeyRSA();
return this;
}
+ case KeyExchangeAlgorithm.SM2:
+ {
+ // validateKeyUsage(KeyUsage.keyEncipherment);
+ pubKeyEC = getPubKeySM2();
+ return this;
+ }
}
}
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java
index 2352200a02..956ac76cf5 100644
--- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java
@@ -22,25 +22,14 @@
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.crypto.encodings.PKCS1Encoding;
-import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.engines.ARIAEngine;
-import org.bouncycastle.crypto.engines.CamelliaEngine;
-import org.bouncycastle.crypto.engines.DESedeEngine;
-import org.bouncycastle.crypto.engines.RC4Engine;
-import org.bouncycastle.crypto.engines.RSABlindedEngine;
-import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.modes.CCMBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
-import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-import org.bouncycastle.crypto.params.SRP6GroupParameters;
+import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.prng.DigestRandomGenerator;
import org.bouncycastle.tls.AlertDescription;
import org.bouncycastle.tls.EncryptionAlgorithm;
@@ -67,14 +56,7 @@
import org.bouncycastle.tls.crypto.TlsSRP6VerifierGenerator;
import org.bouncycastle.tls.crypto.TlsSRPConfig;
import org.bouncycastle.tls.crypto.TlsSecret;
-import org.bouncycastle.tls.crypto.impl.AbstractTlsCrypto;
-import org.bouncycastle.tls.crypto.impl.TlsAEADCipher;
-import org.bouncycastle.tls.crypto.impl.TlsAEADCipherImpl;
-import org.bouncycastle.tls.crypto.impl.TlsBlockCipher;
-import org.bouncycastle.tls.crypto.impl.TlsBlockCipherImpl;
-import org.bouncycastle.tls.crypto.impl.TlsEncryptor;
-import org.bouncycastle.tls.crypto.impl.TlsImplUtils;
-import org.bouncycastle.tls.crypto.impl.TlsNullCipher;
+import org.bouncycastle.tls.crypto.impl.*;
import org.bouncycastle.util.Arrays;
/**
@@ -162,6 +144,9 @@ public TlsCipher createCipher(TlsCryptoParameters cryptoParams, int encryptionAl
case EncryptionAlgorithm.CHACHA20_POLY1305:
// NOTE: Ignores macAlgorithm
return createChaCha20Poly1305(cryptoParams);
+ case EncryptionAlgorithm.SM4_CBC:
+ // Chinese GMSSL SM4 mode
+ return createSM4Cipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.NULL:
return createNullCipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.SEED_CBC:
@@ -195,28 +180,25 @@ protected TlsEncryptor createEncryptor(TlsCertificate certificate)
BcTlsCertificate bcCert = BcTlsCertificate.convert(this, certificate);
bcCert.validateKeyUsage(KeyUsage.keyEncipherment);
- final RSAKeyParameters pubKeyRSA = bcCert.getPubKeyRSA();
- return new TlsEncryptor()
+ final AsymmetricKeyParameter publicKey = bcCert.getPublicKey();
+
+ if(publicKey instanceof RSAKeyParameters)
{
- public byte[] encrypt(byte[] input, int inOff, int length)
- throws IOException
- {
- try
- {
- PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine());
- encoding.init(true, new ParametersWithRandom(pubKeyRSA, getSecureRandom()));
- return encoding.processBlock(input, inOff, length);
- }
- catch (InvalidCipherTextException e)
- {
- /*
- * This should never happen, only during decryption.
- */
- throw new TlsFatalAlert(AlertDescription.internal_error, e);
- }
- }
- };
+ final RSAKeyParameters pubKeyRSA = (RSAKeyParameters) publicKey;
+ return new BcTlsRSAEncryptor(pubKeyRSA, getSecureRandom());
+ }
+ else if(publicKey instanceof ECPublicKeyParameters)
+ {
+ final ECPublicKeyParameters pubKeySM2 = (ECPublicKeyParameters) publicKey;
+ return new BcGmsslEncryptor(pubKeySM2, getSecureRandom());
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+
}
public TlsNonceGenerator createNonceGenerator(byte[] additionalSeedMaterial)
@@ -376,6 +358,8 @@ public Digest createDigest(short hashAlgorithm)
return new SHA384Digest();
case HashAlgorithm.sha512:
return new SHA512Digest();
+ case HashAlgorithm.sm3:
+ return new SM3Digest();
default:
throw new IllegalArgumentException("invalid HashAlgorithm: " + HashAlgorithm.getText(hashAlgorithm));
}
@@ -437,11 +421,22 @@ public static Digest cloneDigest(short hashAlgorithm, Digest hash)
return new SHA384Digest((SHA384Digest)hash);
case HashAlgorithm.sha512:
return new SHA512Digest((SHA512Digest)hash);
+ case HashAlgorithm.sm3:
+ return new SM3Digest((SM3Digest)hash);
default:
throw new IllegalArgumentException("invalid HashAlgorithm: " + HashAlgorithm.getText(hashAlgorithm));
}
}
+ protected TlsCipher createSM4Cipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
+ throws IOException
+ {
+ // SM4 Block size 128bit => 16 byte
+ return new TlsBlockCipher(this, cryptoParams, new BlockOperator(createSM4BlockCipher(), true),
+ new BlockOperator(createSM4BlockCipher(), false), createMAC(cryptoParams, macAlgorithm),
+ createMAC(cryptoParams, macAlgorithm), 16);
+ }
+
protected TlsCipher createAESCipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException
{
@@ -524,6 +519,11 @@ protected TlsBlockCipher createSEEDCipher(TlsCryptoParameters cryptoParams, int
createMAC(cryptoParams, macAlgorithm), 16);
}
+ protected BlockCipher createSM4Engine()
+ {
+ return new SM4Engine();
+ }
+
protected BlockCipher createAESEngine()
{
return new AESEngine();
@@ -544,6 +544,11 @@ protected BlockCipher createAESBlockCipher()
return new CBCBlockCipher(createAESEngine());
}
+ protected BlockCipher createSM4BlockCipher()
+ {
+ return new CBCBlockCipher(createSM4Engine());
+ }
+
protected BlockCipher createARIABlockCipher()
{
return new CBCBlockCipher(createARIAEngine());
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Verifier.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Verifier.java
new file mode 100644
index 0000000000..2de5291c13
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Verifier.java
@@ -0,0 +1,37 @@
+package org.bouncycastle.tls.crypto.impl.bc;
+
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.signers.SM2Signer;
+import org.bouncycastle.tls.DigitallySigned;
+
+/**
+ * SM2 signature verifier
+ *
+ * force use SM2WithSM3 signature verify data.
+ *
+ *
+ */
+public class BcTlsSM2Verifier extends BcTlsVerifier
+{
+ protected BcTlsSM2Verifier(BcTlsCrypto crypto, ECPublicKeyParameters publicKey)
+ {
+ super(crypto, publicKey);
+ }
+
+ /**
+ * verify signature
+ *
+ * @param signedParams signature
+ * @param hash raw message
+ * @return true/false (pass or not)
+ */
+ public boolean verifyRawSignature(DigitallySigned signedParams, byte[] hash)
+ {
+ // ignore SignatureAndHashAlgorithm force use SM2WithSM3 signature alg.
+ Signer signer = new SM2Signer();
+ signer.init(false, publicKey);
+ signer.update(hash, 0, hash.length);
+ return signer.verifySignature(signedParams.getSignature());
+ }
+}
diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/SM2Cipher.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/SM2Cipher.java
new file mode 100644
index 0000000000..074d2cf0ba
--- /dev/null
+++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/SM2Cipher.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.tls.crypto.impl.bc;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.util.BigIntegers;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+/**
+ * GMT 0009-2012
+ *
+ * sm2 encrypted data specific struct
+ *
+ *
+ * @since 2021-03-10 13:28:12
+ */
+public class SM2Cipher extends ASN1Object
+{
+ /*
+ * SM2Cipher ::== SEQUENCE{
+ * XCoordinate INTEGER, --X Portion
+ * YCoordinate INTEGER, --Y Portion
+ * HASH OCTET STRING SIZE(32), --Plaintext sm3 hash
+ * CipherText OCTET STRING --CipherText
+ * }
+ */
+
+ private ASN1Integer xCoordinate;
+ private ASN1Integer yCoordinate;
+ private ASN1OctetString hash;
+ private ASN1OctetString cipherText;
+
+ public SM2Cipher()
+ {
+ super();
+ }
+
+ public SM2Cipher(ASN1Sequence seq)
+ {
+ Enumeration> e = seq.getObjects();
+ xCoordinate = ASN1Integer.getInstance(e.nextElement());
+ yCoordinate = ASN1Integer.getInstance(e.nextElement());
+ hash = ASN1OctetString.getInstance(e.nextElement());
+ cipherText = ASN1OctetString.getInstance(e.nextElement());
+ }
+
+ public static SM2Cipher getInstance(Object o)
+ {
+ if(o instanceof SM2Cipher)
+ {
+ return (SM2Cipher) o;
+ }
+ else if(o != null)
+ {
+ return new SM2Cipher(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+
+ public ASN1Integer getxCoordinate()
+ {
+ return xCoordinate;
+ }
+
+ public void setxCoordinate(ASN1Integer xCoordinate)
+ {
+ this.xCoordinate = xCoordinate;
+ }
+
+ public ASN1Integer getyCoordinate()
+ {
+ return yCoordinate;
+ }
+
+ public void setyCoordinate(ASN1Integer yCoordinate)
+ {
+ this.yCoordinate = yCoordinate;
+ }
+
+ public ASN1OctetString getHash()
+ {
+ return hash;
+ }
+
+ public void setHash(ASN1OctetString hash)
+ {
+ this.hash = hash;
+ }
+
+ public ASN1OctetString getCipherText()
+ {
+ return cipherText;
+ }
+
+ public void setCipherText(ASN1OctetString cipherText)
+ {
+ this.cipherText = cipherText;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector(4);
+ v.add(xCoordinate);
+ v.add(yCoordinate);
+ v.add(hash);
+ v.add(cipherText);
+ return new DERSequence(v);
+ }
+
+ /**
+ * Convert ASN.1 Struct to C1C3C2 format
+ *
+ * @return C1C3C2
+ * @throws IOException
+ */
+ public byte[] convertC1C3C2() throws IOException
+ {
+ /*
+ * construct GMT0009-2012 encrypted data struct
+ */
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+
+ final byte[] x = new byte[32];
+ final byte[] y = new byte[32];
+
+ byte[] tmp = BigIntegers.asUnsignedByteArray(getxCoordinate().getValue());
+ System.arraycopy(tmp, 0, x, 32 - tmp.length, tmp.length);
+ tmp = BigIntegers.asUnsignedByteArray(getyCoordinate().getValue());
+ System.arraycopy(tmp, 0, y, 32 - tmp.length, tmp.length);
+
+ // C1
+ // read 1 byte for uncompressed point prefix 0x04
+ stream.write(0x04);
+ stream.write(x);
+ stream.write(y);
+ // C3
+ stream.write(getHash().getOctets());
+ // C2
+ stream.write(getCipherText().getOctets());
+ stream.flush();
+ return stream.toByteArray();
+ }
+
+ /**
+ * Convert SM2 encrypted result format of c1c3c2 to ASN.1 SM2Cipher
+ *
+ * @param c1c3c2 encrypted result
+ * @return SM2Cipher
+ * @throws IOException
+ */
+ static public SM2Cipher fromC1C3C2(byte[] c1c3c2) throws IOException
+ {
+ /*
+ * construct GMT0009-2012 encrypted data struct
+ */
+ ByteArrayInputStream stream = new ByteArrayInputStream(c1c3c2);
+ // read 1 byte for uncompressed point prefix 0x04
+ stream.read();
+ final byte[] x = new byte[32];
+ final byte[] y = new byte[32];
+ final byte[] hash = new byte[32];
+ int length = c1c3c2.length - 1 - 32 - 32 - 32;
+ final byte[] cipherText = new byte[length];
+ stream.read(x);
+ stream.read(y);
+ stream.read(hash);
+ stream.read(cipherText);
+
+ final SM2Cipher sm2Cipher = new SM2Cipher();
+ sm2Cipher.setxCoordinate(new ASN1Integer(new BigInteger(1, x)));
+ sm2Cipher.setyCoordinate(new ASN1Integer(new BigInteger(1, y)));
+ sm2Cipher.setHash(new DEROctetString(hash));
+ sm2Cipher.setCipherText(new DEROctetString(cipherText));
+ return sm2Cipher;
+ }
+}
diff --git a/tls/src/test/java/org/bouncycastle/tls/test/GMSSLClientTest.java b/tls/src/test/java/org/bouncycastle/tls/test/GMSSLClientTest.java
new file mode 100644
index 0000000000..f8cab2696b
--- /dev/null
+++ b/tls/src/test/java/org/bouncycastle/tls/test/GMSSLClientTest.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.tls.test;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
+import org.bouncycastle.jsse.provider.gm.GMSimpleSSLClient;
+import org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketFactory;
+import org.bouncycastle.tls.TlsClientProtocol;
+import org.bouncycastle.util.io.Streams;
+
+import javax.net.ssl.*;
+import java.io.IOException;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.URL;
+import java.security.*;
+
+/**
+ * Test GMSSL's client connection status
+ *
+ *
+ * @since 2021-03-05 11:31:29
+ */
+public class GMSSLClientTest
+{
+
+ public static void main(String[] args)
+ throws Exception {
+ final BouncyCastleProvider provider = new BouncyCastleProvider();
+ Security.addProvider(provider);
+ Security.addProvider(new BouncyCastleJsseProvider());
+
+// String host = "localhost";
+// int port = 5557;
+// String host = "sm2test.ovssl.cn";
+// int port = 443;
+// bc(host, port);
+// jsse(host, port);
+ HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
+ {
+ public boolean verify(String s, SSLSession sslSession)
+ {
+ return true;
+ }
+ });
+ httpGet("https://sm2test.ovssl.cn/");
+ }
+
+ private static void bc(String host, int port) throws IOException
+ {
+ final GMSimpleSSLClient client = new GMSimpleSSLClient();
+ Socket s = new Socket(host, port);
+ TlsClientProtocol protocol = new TlsClientProtocol(s.getInputStream(), s.getOutputStream());
+ protocol.connect(client);
+
+
+ OutputStream out = protocol.getOutputStream();
+ String req = "GET / HTTP/1.1\r\n" +
+ "Host: "+ host + "\r\n" +
+ "Connection: close\r\n\r\n";
+
+ out.write(req.getBytes("UTF-8"));
+ out.flush();
+ Streams.pipeAll(protocol.getInputStream(), System.out);
+ out.close();
+ }
+
+ private static void jsse(String host, int port) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException
+ {
+ SSLContext clientContext = SSLContext.getInstance("TLS", BouncyCastleJsseProvider.PROVIDER_NAME);
+ clientContext.init(new KeyManager[]{}, new TrustManager[]{}, new SecureRandom());
+ SSLSocketFactory fact = clientContext.getSocketFactory();
+ SSLSocket cSock = (SSLSocket) fact.createSocket(host, port);
+
+ OutputStream out = cSock.getOutputStream();
+ String req = "GET / HTTP/1.1\r\n" +
+ "Host: "+host+"\r\n" +
+ "Connection: close\r\n\r\n";
+
+ out.write(req.getBytes("UTF-8"));
+ out.flush();
+ InputStream in = cSock.getInputStream();
+ byte[] buffer = new byte[2048];
+ in.read(buffer);
+ System.out.println(new String(buffer));
+
+ out.close();
+ in.close();
+ }
+
+
+ private static void httpGet(String urlStr)
+ {
+
+ HttpsURLConnection connection = null;
+
+ try
+ {
+ URL url = new URL(urlStr);
+ connection = (HttpsURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.setSSLSocketFactory(new GMSimpleSSLSocketFactory());
+ final InputStream input = connection.getInputStream();
+ byte[] buff = new byte[4096];
+
+ int n;
+ while((n=input.read(buff)) != -1)
+ {
+ System.out.write(buff, 0, n);
+ }
+ input.close();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ if (connection != null)
+ {
+ connection.disconnect();
+ }
+ }
+ }
+}
diff --git a/tls/src/test/java/org/bouncycastle/tls/test/GMSSLServerTest.java b/tls/src/test/java/org/bouncycastle/tls/test/GMSSLServerTest.java
new file mode 100644
index 0000000000..c42b2b5821
--- /dev/null
+++ b/tls/src/test/java/org/bouncycastle/tls/test/GMSSLServerTest.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.tls.test;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.jsse.provider.gm.GMSimpleSSLServer;
+import org.bouncycastle.tls.*;
+import org.bouncycastle.tls.crypto.TlsCertificate;
+import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto;
+
+import java.io.*;
+import java.net.*;
+import java.security.SecureRandom;
+
+/**
+ * A simple test designed to conduct a GMSSL handshake with an external GMSSL client.
+ *
+ *
+ * @since 2021-03-16 09:57:58
+ */
+public class GMSSLServerTest
+{
+ static BcTlsCrypto crypto;
+ static AsymmetricKeyParameter signKey;
+ static AsymmetricKeyParameter encKey;
+ static Certificate certList;
+
+ public static void main(String[] args) throws Exception
+ {
+
+ int port = 5559;
+ ServerSocket ss = new ServerSocket(port, 16);
+
+ crypto = new BcTlsCrypto(new SecureRandom());
+
+ certList = new Certificate(new TlsCertificate[]{
+ TlsTestUtils.loadCertificateResource(crypto, "x509-server-sm2-sign.pem"),
+ TlsTestUtils.loadCertificateResource(crypto, "x509-server-sm2-enc.pem"),
+ });
+
+ signKey = TlsTestUtils.loadBcPrivateKeyResource("x509-server-key-sm2-sign.pem");
+ encKey = TlsTestUtils.loadBcPrivateKeyResource("x509-server-key-sm2-enc.pem");
+
+ try
+ {
+ while (true)
+ {
+ Socket s = ss.accept();
+ System.out.println("--------------------------------------------------------------------------------");
+ System.out.println("Accepted " + s);
+ ServerThread t = new ServerThread(s);
+ t.start();
+ }
+ } finally
+ {
+ ss.close();
+ }
+ }
+
+ static class ServerThread extends Thread
+ {
+ private final Socket s;
+
+ ServerThread(Socket s)
+ {
+ this.s = s;
+ }
+
+ public void run()
+ {
+ try
+ {
+ GMSimpleSSLServer server = new GMSimpleSSLServer(crypto, certList, signKey, encKey);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(s.getInputStream(), s.getOutputStream());
+ serverProtocol.accept(server);
+
+ final InputStream in = serverProtocol.getInputStream();
+ InputStreamReader isr = new InputStreamReader(in);
+ BufferedReader br = new BufferedReader(isr);
+ System.out.println(">> Request:\n");
+ String lineContent = null;
+ while ((lineContent = br.readLine()) != null)
+ {
+ if(lineContent.length() == 0)
+ {
+ break;
+ }
+ System.out.println("> " + lineContent);
+ }
+ System.out.println();
+
+ OutputStream outputStream = serverProtocol.getOutputStream();
+ String resp = "HTTP/1.1 200 OK\r\n" + "Content-Length: 6\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "\r\n" + "hello\n";
+ outputStream.write(resp.getBytes("UTF-8"));
+ outputStream.flush();
+ serverProtocol.close();
+ System.out.println(">> Responded");
+ } catch (Exception e)
+ {
+ if(e instanceof EOFException)
+ {
+ return;
+ }
+ throw new RuntimeException(e);
+ } finally
+ {
+ try
+ {
+ s.close();
+ } catch (IOException e)
+ {
+ } finally
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/tls/src/test/java/org/bouncycastle/tls/test/GMSimpleSSLSocketFactoryTest.java b/tls/src/test/java/org/bouncycastle/tls/test/GMSimpleSSLSocketFactoryTest.java
new file mode 100644
index 0000000000..4e552f352a
--- /dev/null
+++ b/tls/src/test/java/org/bouncycastle/tls/test/GMSimpleSSLSocketFactoryTest.java
@@ -0,0 +1,169 @@
+package org.bouncycastle.tls.test;
+
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.jsse.provider.gm.GMSimpleSSLSocketFactory;
+import org.bouncycastle.tls.Certificate;
+import org.bouncycastle.tls.crypto.TlsCertificate;
+import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto;
+import org.bouncycastle.util.io.Streams;
+
+import java.io.*;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+public class GMSimpleSSLSocketFactoryTest
+{
+ static final int port = 5558;
+
+ public static void main(String[] args) throws IOException, InterruptedException
+ {
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ final Certificate certList = new Certificate(new TlsCertificate[]{
+ TlsTestUtils.loadCertificateResource(crypto, "x509-server-sm2-sign.pem"),
+ TlsTestUtils.loadCertificateResource(crypto, "x509-server-sm2-enc.pem"),
+ });
+ final AsymmetricKeyParameter signKey = TlsTestUtils.loadBcPrivateKeyResource("x509-server-key-sm2-sign.pem");
+ final AsymmetricKeyParameter encKey = TlsTestUtils.loadBcPrivateKeyResource("x509-server-key-sm2-enc.pem");
+
+
+ bootServer(port, certList, signKey, encKey);
+// bootClient("sm2test.ovssl.cn", 443);
+
+ }
+
+ /*
+ // GMSSL HttpClient 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;
+ public class GMHttpClient {
+ public static void main(String[] args) throws Exception {
+ Security.addProvider(new BouncyCastleProvider());
+ Security.addProvider(new BouncyCastleJsseProvider());
+
+ 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:5558"));
+ response.getEntity().writeTo(System.out);
+ }
+ }
+ */
+
+ private static void bootServer(int port, Certificate certList, AsymmetricKeyParameter signKey, AsymmetricKeyParameter encKey)
+ {
+ ServerSocket ss = null;
+ try
+ {
+ final GMSimpleSSLSocketFactory socketFactory = GMSimpleSSLSocketFactory.ServerFactory(certList, signKey, encKey);
+ ss = new ServerSocket(port, 16);
+
+ while (true)
+ {
+ Socket s = ss.accept();
+ System.out.println("--------------------------------------------------------------------------------");
+ System.out.println("Accepted " + s);
+ s = socketFactory.createSocket(s, "", 0, true);
+ new Thread(new ServerThread(s)).start();
+ }
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ } finally
+ {
+ try
+ {
+ if(ss != null)
+ {
+ ss.close();
+ }
+ } catch (IOException e)
+ {
+ }
+ }
+ }
+
+ private static void bootClient(String host, int port) throws IOException
+ {
+ final GMSimpleSSLSocketFactory socketFactory = GMSimpleSSLSocketFactory.ClientFactory();
+ final Socket cSock = socketFactory.createSocket(host, port);
+ OutputStream out = cSock.getOutputStream();
+ String req = "GET / HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n";
+
+ out.write(req.getBytes("UTF-8"));
+ out.flush();
+ InputStream in = cSock.getInputStream();
+ Streams.pipeAll(in, System.out);
+ out.close();
+ in.close();
+ }
+
+ static class ServerThread implements Runnable
+ {
+ private final Socket socket;
+
+ public ServerThread(Socket s)
+ {
+ this.socket = s;
+ }
+
+ public void run()
+ {
+ try
+ {
+ InputStreamReader isr = new InputStreamReader(socket.getInputStream());
+ BufferedReader br = new BufferedReader(isr);
+ System.out.println(">> Request:\n");
+ String lineContent = null;
+ while ((lineContent = br.readLine()) != null)
+ {
+ if(lineContent.length() == 0)
+ {
+ break;
+ }
+ System.out.println("> " + lineContent);
+ }
+ System.out.println();
+
+ OutputStream outputStream = socket.getOutputStream();
+ String resp = "HTTP/1.1 200 OK\r\n" + "Content-Length: 6\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "\r\n" + "hello\n";
+ outputStream.write(resp.getBytes("UTF-8"));
+ outputStream.flush();
+ socket.close();
+ System.out.println(">> Responded");
+ } catch (IOException e)
+ {
+ if(e instanceof EOFException)
+ {
+ return;
+ }
+ e.printStackTrace();
+ } finally
+ {
+ try
+ {
+ socket.close();
+ } catch (IOException e)
+ {
+
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-sm2.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-sm2.pem
new file mode 100644
index 0000000000..d1433c53f3
--- /dev/null
+++ b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-sm2.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgPja9Wd7sQOI2ZEis
+h1FZbHcsO4NUxqT9lSDiI9pQOBmgCgYIKoEcz1UBgi2hRANCAARnMDPZoNh72vvL
+bO+8W5QjkV5wcOZormBLJf+ZCAi59jqJApvUUtmz9m/ALJMeIVur0oVUsvlI/hKz
+vUvnv3XK
+-----END PRIVATE KEY-----
diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-sm2.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-sm2.pem
new file mode 100644
index 0000000000..f8c16e8591
--- /dev/null
+++ b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-sm2.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB5DCCAYmgAwIBAgIIAs4Dt8fd5ncwCgYIKoEcz1UBg3UwRTELMAkGA1UEBhMC
+Q04xETAPBgNVBAgTCFpoZWppYW5nMREwDwYDVQQHEwhIYW5nemhvdTEQMA4GA1UE
+ChMHVGVzdCBDQTAeFw0yMTAzMTIwMzQ1MzVaFw0zMTAzMTIwMzQ1MzVaMEUxCzAJ
+BgNVBAYTAkNOMREwDwYDVQQIEwhaaGVqaWFuZzERMA8GA1UEBxMISGFuZ3pob3Ux
+EDAOBgNVBAoTB1Rlc3QgQ0EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAARnMDPZ
+oNh72vvLbO+8W5QjkV5wcOZormBLJf+ZCAi59jqJApvUUtmz9m/ALJMeIVur0oVU
+svlI/hKzvUvnv3XKo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUJzDbP6IFeVDlegj4rMuWgPNPw8MwHwYDVR0jBBgwFoAUJzDb
+P6IFeVDlegj4rMuWgPNPw8MwCgYIKoEcz1UBg3UDSQAwRgIhAIk4RUSafZ/Mx6Fi
+vD0dzrehJyZ3NdTgfkpaGBtyU+xCAiEA5Pv54SXiCAH5RwYQbaCxCtml8NTn3WVd
+DVM1URKXiNQ=
+-----END CERTIFICATE-----
diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-sm2-enc.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-sm2-enc.pem
new file mode 100644
index 0000000000..c432275800
--- /dev/null
+++ b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-sm2-enc.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgOwrsOroddxkoOYoE
+xZlbDdPsrSY6ug08Saf/IkMSbxegCgYIKoEcz1UBgi2hRANCAARB5CzonabehIxs
+RegFs1Vs3IZrXJXzZIPH2F1afv/NLj3OSngVUMbGK0sTOSfdn1wLKiLAvvzYuHgb
+sA3LAW/E
+-----END PRIVATE KEY-----
diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-sm2-sign.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-sm2-sign.pem
new file mode 100644
index 0000000000..3619dbfba6
--- /dev/null
+++ b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-sm2-sign.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgsanX/ZPknQYj5Ihn
+J394MHsOBFB7GI2QuqsOWm4B+kGgCgYIKoEcz1UBgi2hRANCAATdyAq+Ik8vkBPp
+CyMn7Q+uRiipEvNWVNm+3+q5zM4oisxMuneyCt51HlnA2Iom9T7tL25ejnMYt0UB
+L4Aj1aaU
+-----END PRIVATE KEY-----
diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-sm2-enc.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-sm2-enc.pem
new file mode 100644
index 0000000000..5751eb0d0b
--- /dev/null
+++ b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-sm2-enc.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB7TCCAZOgAwIBAgIIAs4Dt8ftfmcwCgYIKoEcz1UBg3UwRTELMAkGA1UEBhMC
+Q04xETAPBgNVBAgTCFpoZWppYW5nMREwDwYDVQQHEwhIYW5nemhvdTEQMA4GA1UE
+ChMHVGVzdCBDQTAeFw0yMTAzMTIwMzQ2MzdaFw0zMTAzMTIwMzQ1MzVaMGAxCzAJ
+BgNVBAYTAkNOMREwDwYDVQQIEwhaaGVqaWFuZzERMA8GA1UEBxMISGFuZ3pob3Ux
+ETAPBgNVBAoTCHRlc3Qgb3JnMRgwFgYDVQQDDA9URVNUX1RMU19TRVJWRVIwWTAT
+BgcqhkjOPQIBBggqgRzPVQGCLQNCAARB5CzonabehIxsRegFs1Vs3IZrXJXzZIPH
+2F1afv/NLj3OSngVUMbGK0sTOSfdn1wLKiLAvvzYuHgbsA3LAW/Eo1IwUDAOBgNV
+HQ8BAf8EBAMCBDAwHQYDVR0OBBYEFEmiOBKBDtDro5jJ45jyKG2NNjhFMB8GA1Ud
+IwQYMBaAFCcw2z+iBXlQ5XoI+KzLloDzT8PDMAoGCCqBHM9VAYN1A0gAMEUCIQDd
+XGPbrR1DZkqeXzkqAIlsLzXfS61EqW9mH/xkhD0UvgIgBKg3S1uRS4YL25ZC472v
+Dl6tCE72cMM6xnBzSwQl6oY=
+-----END CERTIFICATE-----
diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-sm2-sign.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-sm2-sign.pem
new file mode 100644
index 0000000000..24148aad6e
--- /dev/null
+++ b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-sm2-sign.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICDTCCAbKgAwIBAgIIAs4Dt8ftdtcwCgYIKoEcz1UBg3UwRTELMAkGA1UEBhMC
+Q04xETAPBgNVBAgTCFpoZWppYW5nMREwDwYDVQQHEwhIYW5nemhvdTEQMA4GA1UE
+ChMHVGVzdCBDQTAeFw0yMTAzMTIwMzQ2MzdaFw0zMTAzMTIwMzQ1MzVaMGAxCzAJ
+BgNVBAYTAkNOMREwDwYDVQQIEwhaaGVqaWFuZzERMA8GA1UEBxMISGFuZ3pob3Ux
+ETAPBgNVBAoTCHRlc3Qgb3JnMRgwFgYDVQQDDA9URVNUX1RMU19TRVJWRVIwWTAT
+BgcqhkjOPQIBBggqgRzPVQGCLQNCAATdyAq+Ik8vkBPpCyMn7Q+uRiipEvNWVNm+
+3+q5zM4oisxMuneyCt51HlnA2Iom9T7tL25ejnMYt0UBL4Aj1aaUo3EwbzAOBgNV
+HQ8BAf8EBAMCBsAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1Ud
+DgQWBBSGSgh9dcFdcLn3NjQBJpYachVZYzAfBgNVHSMEGDAWgBQnMNs/ogV5UOV6
+CPisy5aA80/DwzAKBggqgRzPVQGDdQNJADBGAiEA6z2Jjhiok+e+Y6tEJlnn3dcE
+kJX+cIBiHkDnjtQOIi0CIQD0nEDlrbC5PyZY9Ydk/dLfmpmjsknNmx3GJgG9MA/z
+VA==
+-----END CERTIFICATE-----