split2list(String str) {
+ return split2list(str, COMMA);
+ }
+
+ public static String join(int[] ints, String sep) {
+ return Arrays.stream(ints).boxed().map(String::valueOf).collect(Collectors.joining(sep));
+ }
+
+ /**
+ * 返回两参数中不为空的参数. 优先返回第一个参数.
+ */
+ public static String getNotEmpty(String v1, String v2) {
+ return isNotEmpty(v1) ? v1 : v2;
+ }
+
+
+ /**
+ * 检查 CharSequence 是否等于给定字符集中的任何字符
+ *
+ *
+ * equalsAny("a", "a", "c") = true
+ * equalsAny("a", "b", "c") = false
+ * equalsAny("a") = false
+ * equalsAny(null) = false
+ * equalsAny(null, null) = false
+ *
+ *
+ * @param cs 要检查的 CharSequence,可能为 null
+ * @param css 要判断的字符,可能为空
+ * @return true 如果等于任何字符, false 如果没有匹配或空输入
+ */
+ public static boolean equalsAny(CharSequence cs, CharSequence... css) {
+ if (isEmpty(cs) || ArrayUtils.isEmpty(css)) {
+ return false;
+ }
+ for (CharSequence ce : css) {
+ if (cs.equals(ce)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static final Pattern LINE_PATTERN = Pattern.compile("_(\\w)");
+ private static final Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
+
+ /**
+ * 驼峰转下划线
+ */
+ public static String hump2line(String str) {
+ if (isEmpty(str)) {
+ return "";
+ }
+ Matcher matcher = HUMP_PATTERN.matcher(str);
+ StringBuffer sb = new StringBuffer();
+ while (matcher.find()) {
+ matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ /**
+ * 下划线转驼峰
+ */
+ public static String line2hump(String str) {
+ if (isEmpty(str)) {
+ return "";
+ }
+ str = str.toLowerCase();
+ Matcher matcher = LINE_PATTERN.matcher(str);
+ StringBuffer sb = new StringBuffer();
+ while (matcher.find()) {
+ matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ /**
+ * 若 str 的长度不足 length, 则左侧补零
+ */
+ public static String addZero(String str, int length) {
+ if (str == null) {
+ str = "";
+ }
+ StringBuilder sb = new StringBuilder();
+ int diff = length - str.length();
+ for (int i = 0; i < diff; i++) {
+ sb.append("0");
+ }
+ return sb + str;
+ }
+
+ /**
+ * 若 i 的长度不足 length, 则左侧补零
+ */
+ public static String addZero(int i, int length) {
+ return addZero(i + "", length);
+ }
+
+ public static String getAbsolutePath(String root, String relativePath) {
+ String sep = "/";
+ String path1 = root.replaceAll("\\\\", sep);
+ String path2 = relativePath.replaceAll("\\\\", sep);
+
+ if (path1.endsWith(sep)) {
+ path1 = path1.substring(0, path1.length() - 1);
+ }
+ String[] split2 = Strings.split(path2, sep);
+ for (String s : split2) {
+ if (s.equals("..")) {
+ path1 = path1.substring(0, path1.lastIndexOf(sep));
+ path2 = path2.substring(path2.indexOf(sep) + 1);
+ } else if (s.equals(".")) {
+ path2 = path2.substring(path2.indexOf(sep) + 1);
+ } else {
+ return path1 + sep + path2;
+ }
+ }
+ throw new RuntimeException("ERR");
+ }
+}
diff --git a/src/main/java/cc/kkon/gmhttps/utils/Utils.java b/src/main/java/cc/kkon/gmhttps/utils/Utils.java
new file mode 100644
index 0000000..3119d68
--- /dev/null
+++ b/src/main/java/cc/kkon/gmhttps/utils/Utils.java
@@ -0,0 +1,43 @@
+package cc.kkon.gmhttps.utils;
+
+import cc.kkon.gmhttps.model.FirstLine;
+import cc.kkon.gmhttps.utils.Strings;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Utils {
+
+ public static Map buildHeaders(List headLine) {
+ Map res = new HashMap<>();
+ for (String line : headLine) {
+ String[] split = line.split(":");
+ if (split.length == 2) {
+ res.put(split[0], split[1].trim());
+ }
+ }
+ return res;
+ }
+
+ public static FirstLine parse1stLine(String firstLine) {
+ String[] split = Strings.split(firstLine, " ");
+ FirstLine fl = new FirstLine();
+ fl.method = split[0];
+ fl.url = split[1];
+ fl.version = split[2];
+ return fl;
+ }
+
+ public static Map parseParams(String paramsStr) {
+ Map params = new HashMap<>();
+ String[] ss = Strings.split(paramsStr, "&");
+ for (String s : ss) {
+ String[] split = Strings.split(s, "=");
+ if (split.length == 2) {
+ params.put(split[0], split[1]);
+ }
+ }
+ return params;
+ }
+}
diff --git a/src/test/java/cc/kkon/AppTest.java b/src/test/java/cc/kkon/AppTest.java
new file mode 100644
index 0000000..b5dfb64
--- /dev/null
+++ b/src/test/java/cc/kkon/AppTest.java
@@ -0,0 +1,67 @@
+package cc.kkon;
+
+
+import cc.kkon.gmhttps.client.Response0;
+import cc.kkon.gmhttps.client.SSLRequests;
+import cc.kkon.gmhttps.server.SSLServer;
+import org.junit.Test;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest {
+
+
+ @Test
+ public void testClient() throws Exception {
+ String url = "";
+ // url = "https://ebssec.boc.cn/";
+ url = "https://localhost:4430/get1";
+
+ Map params = new HashMap<>();
+ Map headers = new HashMap<>();
+
+ params.put("ip", "192.168.1.1");
+ params.put("pwd", "12345678");
+ headers.put("token", UUID.randomUUID().toString());
+
+ Response0 r2 = SSLRequests.get(url, params, headers);
+ System.out.println("r2.getHeader(\"app-id\") = " + r2.getHeader("app-id"));
+ System.out.println("r2.getContent() = " + r2.getContent());
+
+ params.put("post1", "begin--abc--end");
+ params.put("post2", UUID.randomUUID().toString());
+ url = "https://localhost:4430/post1";
+ SSLRequests.post(url, params, headers);
+
+ String json = "{" +
+ "\"a\": \"abc\"," +
+ "\"b\": 123" +
+ "}";
+ Response0 r3 = SSLRequests.post4json(url, json, headers);
+ System.out.println();
+ }
+
+ @Test
+ public void testServer() throws Exception {
+ String cert = "keystore/sm2.server1.both.pfx";
+ cert = "certs/sm2.auth1.both.pfx";
+ InputStream in = getClass().getClassLoader().getResourceAsStream(cert);
+ String pwd = "12345678";
+ SSLServer server = new SSLServer(4430, in, pwd);
+
+ server.addServlet("/get1", new TestServlet1());
+ server.addServlet(new TestServlet2());
+
+ server.listen();
+
+ Thread.currentThread().join();
+ }
+
+
+}
diff --git a/src/test/java/cc/kkon/TestServlet1.java b/src/test/java/cc/kkon/TestServlet1.java
new file mode 100644
index 0000000..b425adf
--- /dev/null
+++ b/src/test/java/cc/kkon/TestServlet1.java
@@ -0,0 +1,37 @@
+package cc.kkon;
+
+import cc.kkon.gmhttps.server.DefaultHttpServlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+
+public class TestServlet1 extends DefaultHttpServlet {
+
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ System.out.println("get");
+ this.doPost(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ System.out.println("post");
+ String ip = req.getParameter("ip");
+ String pwd = req.getParameter("pwd");
+ String post1 = req.getParameter("post1");
+ String post2 = req.getParameter("post2");
+ String token = req.getHeader("token");
+
+ resp.setHeader("app-id", UUID.randomUUID().toString());
+ ServletOutputStream out = resp.getOutputStream();
+ out.write("Hello 世界!".getBytes(StandardCharsets.UTF_8));
+ out.flush();
+ System.out.println();
+ }
+}
diff --git a/src/test/java/cc/kkon/TestServlet2.java b/src/test/java/cc/kkon/TestServlet2.java
new file mode 100644
index 0000000..ba799e8
--- /dev/null
+++ b/src/test/java/cc/kkon/TestServlet2.java
@@ -0,0 +1,41 @@
+package cc.kkon;
+
+import cc.kkon.gmhttps.server.DefaultHttpServlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+
+@WebServlet("/post2")
+public class TestServlet2 extends DefaultHttpServlet {
+
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ System.out.println("get");
+ this.doPost(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ System.out.println("post");
+ String ip = req.getParameter("ip");
+ String pwd = req.getParameter("pwd");
+ String post1 = req.getParameter("post1");
+ String post2 = req.getParameter("post2");
+ String token = req.getHeader("token");
+
+ // req.get
+
+ resp.setHeader("app-id", UUID.randomUUID().toString());
+ ServletOutputStream out = resp.getOutputStream();
+ out.write("Hello 世界!".getBytes(StandardCharsets.UTF_8));
+ out.flush();
+ System.out.println();
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/client/Client1.java b/src/test/java/cc/kkon/gmssl_cn/client/Client1.java
new file mode 100644
index 0000000..c8a90bc
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/client/Client1.java
@@ -0,0 +1,102 @@
+package cc.kkon.gmssl_cn.client;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.*;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+
+/**
+ * 单向认证
+ *
+ * @author gmssl.cn
+ */
+public class Client1 {
+ public Client1() {
+ }
+
+ public static void main(String[] args) {
+ SocketFactory fact = null;
+ SSLSocket socket = null;
+
+
+ System.out.println("Usage: java -cp GMExample.jar client.Client1 addr port");
+
+ try {
+ String addr = "ebssec.boc.cn";
+ addr = "demo.gmssl.cn";
+ int port = 444;
+ String uri = "/";
+ if (args.length > 0) {
+ addr = args[0];
+ port = Integer.parseInt(args[1]);
+ }
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ fact = createSocketFactory(null, null);
+ socket = (SSLSocket) fact.createSocket();
+ socket.setEnabledCipherSuites(new String[]{"ECC_SM4_CBC_SM3"});
+ socket.setTcpNoDelay(true);
+
+ socket.connect(new InetSocketAddress(addr, port), 2000);
+ socket.setTcpNoDelay(true);
+ socket.startHandshake();
+
+ DataInputStream in = new DataInputStream(socket.getInputStream());
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+ String s = "GET " + uri + " HTTP/1.1\r\n";
+ s += "Accept: */*\r\n";
+ s += "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)\r\n";
+ s += "Host: " + addr + (port == 443 ? "" : ":" + port) + "\r\n";
+ s += "Connection: Close\r\n";
+ s += "\r\n";
+ out.write(s.getBytes());
+ out.flush();
+
+ System.out.println(socket.getSession().getCipherSuite());
+
+ byte[] buf = new byte[8192];
+ int len = in.read(buf);
+ if (len == -1) {
+ System.out.println("eof");
+ return;
+ }
+ System.out.println(new String(buf, 0, len));
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
+ TrustAllManager[] trust = {new TrustAllManager()};
+
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ SecureRandom secureRandom = new SecureRandom();
+ ctx.init(kms, trust, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLSocketFactory factory = ctx.getSocketFactory();
+ return factory;
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/client/Client2.java b/src/test/java/cc/kkon/gmssl_cn/client/Client2.java
new file mode 100644
index 0000000..888136b
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/client/Client2.java
@@ -0,0 +1,106 @@
+package cc.kkon.gmssl_cn.client;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.*;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+
+/**
+ * 双向认证
+ *
+ * @author gmssl.cn
+ */
+public class Client2 {
+ public Client2() {
+ }
+
+ public static void main(String[] args) {
+ SocketFactory fact = null;
+ SSLSocket socket = null;
+
+ System.out.println("Usage: java -cp GMExample.jar client.Client2 addr port");
+
+ try {
+ String addr = "demo.gmssl.cn";
+ int port = 1443;
+ String uri = "/";
+ if (args.length > 0) {
+ addr = args[0];
+ port = Integer.parseInt(args[1]);
+ }
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ String pfxfile = "keystore/sm2.user1.both.pfx";
+ String pwd = "12345678";
+ KeyStore pfx = KeyStore.getInstance("PKCS12", "GMJCE");
+ pfx.load(new FileInputStream(pfxfile), pwd.toCharArray());
+
+ fact = createSocketFactory(pfx, pwd.toCharArray());
+ socket = (SSLSocket) fact.createSocket();
+ socket.setEnabledCipherSuites(new String[]{"ECC_SM4_CBC_SM3"});
+ socket.setTcpNoDelay(true);
+
+ socket.connect(new InetSocketAddress(addr, port), 2000);
+ socket.setTcpNoDelay(true);
+ socket.startHandshake();
+
+ DataInputStream in = new DataInputStream(socket.getInputStream());
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+ String s = "GET " + uri + " HTTP/1.1\r\n";
+ s += "Accept: */*\r\n";
+ s += "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)\r\n";
+ s += "Host: " + addr + (port == 443 ? "" : ":" + port) + "\r\n";
+ s += "Connection: Close\r\n";
+ s += "\r\n";
+ out.write(s.getBytes());
+ out.flush();
+
+ System.out.println(socket.getSession().getCipherSuite());
+
+ byte[] buf = new byte[8192];
+ int len = in.read(buf);
+ if (len == -1) {
+ System.out.println("eof");
+ return;
+ }
+ System.out.println(new String(buf, 0, len));
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
+ TrustAllManager[] trust = {new TrustAllManager()};
+
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ SecureRandom secureRandom = new SecureRandom();
+ ctx.init(kms, trust, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLSocketFactory factory = ctx.getSocketFactory();
+ return factory;
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/client/Client3.java b/src/test/java/cc/kkon/gmssl_cn/client/Client3.java
new file mode 100644
index 0000000..f3dc2b7
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/client/Client3.java
@@ -0,0 +1,173 @@
+package cc.kkon.gmssl_cn.client;
+
+import javax.net.ssl.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * 单向认证(使用HttpsURLConnection)
+ *
+ * @author gmssl.cn
+ */
+public class Client3 {
+ public Client3() {
+ }
+
+ public static void main(String[] args) {
+ SSLSocketFactory fact = null;
+ HttpsURLConnection conn = null;
+
+ System.out.println("Usage: java -cp GMExample.jar client.Client3 url");
+
+ try {
+ String urlStr = "https://ebssec.boc.cn/";
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ fact = createSocketFactory(null, null);
+ SSLSocketFactory fact2 = new PreferredCipherSuiteSSLSocketFactory(fact);
+
+ URL url = new URL(urlStr);
+ conn = (HttpsURLConnection) url.openConnection();
+ conn.setInstanceFollowRedirects(true);
+ conn.setSSLSocketFactory(fact2);
+ conn.setHostnameVerifier(new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+
+ conn.connect();
+
+ InputStream input = conn.getInputStream();
+ byte[] buffer = new byte[1024 * 4];
+ int length = 0;
+ while ((length = input.read(buffer)) != -1) {
+ System.out.println(new String(buffer, 0, length));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ conn.disconnect();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
+ TrustAllManager[] trust = {new TrustAllManager()};
+
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ SecureRandom secureRandom = new SecureRandom();
+ ctx.init(kms, trust, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLSocketFactory factory = ctx.getSocketFactory();
+ return factory;
+ }
+}
+
+class PreferredCipherSuiteSSLSocketFactory extends SSLSocketFactory {
+
+ private static final String PREFERRED_CIPHER_SUITE = "ECC_SM4_CBC_SM3";
+
+ private final SSLSocketFactory delegate;
+
+ public PreferredCipherSuiteSSLSocketFactory(SSLSocketFactory delegate) {
+
+ this.delegate = delegate;
+ }
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+
+ return setupPreferredDefaultCipherSuites(this.delegate);
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+
+ return setupPreferredSupportedCipherSuites(this.delegate);
+ }
+
+ @Override
+ public Socket createSocket(String arg0, int arg1) throws IOException {
+
+ Socket socket = this.delegate.createSocket(arg0, arg1);
+ String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
+ ((SSLSocket) socket).setEnabledCipherSuites(cipherSuites);
+
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
+
+ Socket socket = this.delegate.createSocket(arg0, arg1);
+ String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
+ ((SSLSocket) socket).setEnabledCipherSuites(cipherSuites);
+
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3) throws IOException {
+
+ Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3);
+ String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
+ ((SSLSocket) socket).setEnabledCipherSuites(cipherSuites);
+
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
+
+ Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3);
+ String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
+ ((SSLSocket) socket).setEnabledCipherSuites(cipherSuites);
+
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
+
+ Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3);
+ String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
+ ((SSLSocket) socket).setEnabledCipherSuites(cipherSuites);
+
+ return socket;
+ }
+
+ private static String[] setupPreferredDefaultCipherSuites(SSLSocketFactory sslSocketFactory) {
+ ArrayList suitesList = new ArrayList(Arrays.asList(PREFERRED_CIPHER_SUITE));
+ return suitesList.toArray(new String[suitesList.size()]);
+ }
+
+ private static String[] setupPreferredSupportedCipherSuites(SSLSocketFactory sslSocketFactory) {
+ ArrayList suitesList = new ArrayList(Arrays.asList(PREFERRED_CIPHER_SUITE));
+ return suitesList.toArray(new String[suitesList.size()]);
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/client/Client4.java b/src/test/java/cc/kkon/gmssl_cn/client/Client4.java
new file mode 100644
index 0000000..4c7b842
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/client/Client4.java
@@ -0,0 +1,90 @@
+package cc.kkon.gmssl_cn.client;
+
+import javax.net.ssl.*;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+
+/**
+ * 使用HttpsURLConnection(双向)
+ *
+ * @author gmssl.cn
+ */
+public class Client4 {
+ public Client4() {
+ }
+
+ public static void main(String[] args) {
+ SSLSocketFactory fact = null;
+ HttpsURLConnection conn = null;
+
+ System.out.println("Usage: java -cp GMExample.jar client.Client4 url");
+
+ try {
+ String urlStr = "https://demo.gmssl.cn:1443/";
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ String pfxfile = "keystore/sm2.user1.both.pfx";
+ String pwd = "12345678";
+ KeyStore pfx = KeyStore.getInstance("PKCS12", "GMJCE");
+ pfx.load(new FileInputStream(pfxfile), pwd.toCharArray());
+ fact = createSocketFactory(pfx, pwd.toCharArray());
+
+ SSLSocketFactory fact2 = new PreferredCipherSuiteSSLSocketFactory(fact);
+
+ URL url = new URL(urlStr);
+ conn = (HttpsURLConnection) url.openConnection();
+ conn.setInstanceFollowRedirects(true);
+ conn.setSSLSocketFactory(fact2);
+ conn.setHostnameVerifier(new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+
+ conn.connect();
+
+ InputStream input = conn.getInputStream();
+ byte[] buffer = new byte[1024 * 4];
+ int length = 0;
+ while ((length = input.read(buffer)) != -1) {
+ System.out.println(new String(buffer, 0, length));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ conn.disconnect();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
+ TrustAllManager[] trust = {new TrustAllManager()};
+
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ SecureRandom secureRandom = new SecureRandom();
+ ctx.init(kms, trust, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLSocketFactory factory = ctx.getSocketFactory();
+ return factory;
+ }
+}
+
diff --git a/src/test/java/cc/kkon/gmssl_cn/client/Client5.java b/src/test/java/cc/kkon/gmssl_cn/client/Client5.java
new file mode 100644
index 0000000..855658a
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/client/Client5.java
@@ -0,0 +1,131 @@
+package cc.kkon.gmssl_cn.client;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.*;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+/**
+ * 双向认证(验证服务端证书)
+ *
+ * @author gmssl.cn
+ */
+public class Client5 {
+ public Client5() {
+ }
+
+ public static void main(String[] args) {
+ SocketFactory fact = null;
+ SSLSocket socket = null;
+
+ System.out.println("Usage: java -cp GMExample.jar client.Client5 addr port");
+
+ try {
+ String addr = "demo.gmssl.cn";
+ int port = 1443;
+ String uri = "/";
+ if (args.length > 0) {
+ addr = args[0];
+ port = Integer.parseInt(args[1]);
+ }
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ // 客户端密钥对
+ String pfxfile = "keystore/sm2.user1.both.pfx";
+ String pwd = "12345678";
+ KeyStore pfx = KeyStore.getInstance("PKCS12", "GMJSSE");
+ pfx.load(new FileInputStream(pfxfile), pwd.toCharArray());
+
+ // 加载可信证书
+ KeyStore trust = KeyStore.getInstance("PKCS12");
+ trust.load(null);
+ FileInputStream fin = new FileInputStream("keystore/sm2.oca.pem");
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate oca = (X509Certificate) cf.generateCertificate(fin);
+ trust.setCertificateEntry("oca", oca);
+ fin = new FileInputStream("keystore/sm2.rca.pem");
+ X509Certificate rca = (X509Certificate) cf.generateCertificate(fin);
+ trust.setCertificateEntry("rca", rca);
+
+ // 创建Factory
+ fact = createSocketFactory(pfx, pwd.toCharArray(), trust);
+ socket = (SSLSocket) fact.createSocket();
+ socket.setEnabledCipherSuites(new String[]{"ECC_SM4_CBC_SM3"});
+ socket.setTcpNoDelay(true);
+
+ socket.connect(new InetSocketAddress(addr, port), 2000);
+ socket.setTcpNoDelay(true);
+ socket.startHandshake();
+
+ DataInputStream in = new DataInputStream(socket.getInputStream());
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+ String s = "GET " + uri + " HTTP/1.1\r\n";
+ s += "Accept: */*\r\n";
+ s += "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)\r\n";
+ s += "Host: " + addr + (port == 443 ? "" : ":" + port) + "\r\n";
+ s += "Connection: Close\r\n";
+ s += "\r\n";
+ out.write(s.getBytes());
+ out.flush();
+
+ System.out.println(socket.getSession().getCipherSuite());
+
+ byte[] buf = new byte[8192];
+ int len = in.read(buf);
+ if (len == -1) {
+ System.out.println("eof");
+ return;
+ }
+ System.out.println(new String(buf, 0, len));
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd, KeyStore trustStore) throws Exception {
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ TrustManager[] tms = null;
+ if (trustStore != null) {
+ // 指定指定的证书验证
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(trustStore);
+ tms = tmf.getTrustManagers();
+ } else {
+ // 不验证(信任全部)
+ tms = new TrustManager[1];
+ tms[0] = new TrustAllManager();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ SecureRandom secureRandom = new SecureRandom();
+ ctx.init(kms, tms, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLSocketFactory factory = ctx.getSocketFactory();
+ return factory;
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/client/TrustAllManager.java b/src/test/java/cc/kkon/gmssl_cn/client/TrustAllManager.java
new file mode 100644
index 0000000..f4b3a9d
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/client/TrustAllManager.java
@@ -0,0 +1,22 @@
+package cc.kkon.gmssl_cn.client;
+
+import javax.net.ssl.X509TrustManager;
+import java.security.cert.X509Certificate;
+
+public class TrustAllManager implements X509TrustManager {
+ private final X509Certificate[] issuers;
+
+ public TrustAllManager() {
+ this.issuers = new X509Certificate[0];
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return issuers;
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType) {
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType) {
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient1.java b/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient1.java
new file mode 100644
index 0000000..1d625ca
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient1.java
@@ -0,0 +1,207 @@
+package cc.kkon.gmssl_cn.httpclient;
+
+import org.apache.commons.collections4.MapUtils;
+import org.apache.http.*;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.apache.http.message.BasicNameValuePair;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 使用HttpClient访问国密https
+ *
+ * @author gmssl.cn
+ */
+public class HttpClient1 {
+ private static final String ENCODING = "UTF-8";
+ private static HttpClient client = null;
+
+
+ // 创建SSL上下文---忽略服务端证书信任
+ static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException {
+ SSLContext sc = SSLContext.getInstance(cn.gmssl.jsse.provider.GMJSSE.GMSSLv11, cn.gmssl.jsse.provider.GMJSSE.NAME);
+
+ // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
+ X509TrustManager trustManager = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ for (int i = 0; i < paramArrayOfX509Certificate.length; i++) {
+ System.out.println(paramArrayOfX509Certificate[i].getSubjectDN().getName());
+ }
+ System.out.println();
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ for (int i = 0; i < paramArrayOfX509Certificate.length; i++) {
+ System.out.println(paramArrayOfX509Certificate[i].getSubjectDN().getName());
+ }
+ System.out.println();
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+ sc.init(null, new TrustManager[]{trustManager}, null);
+ return sc;
+ }
+
+ private static void initGMSSL() {
+ try {
+ Security.insertProviderAt(new cn.gmssl.jce.provider.GMJCE(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ SSLContext sslContext = createIgnoreVerifySSL();
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
+ new String[]{"GMSSLv1.1"}, new String[]{"ECC_SM4_CBC_SM3"},
+ SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ Registry socketFactoryRegistry =
+ RegistryBuilder.create()
+ .register("https", sslsf).build();
+
+ int timeout = 5;
+ RequestConfig config = RequestConfig.custom()
+ .setConnectTimeout(timeout * 1000)
+ .setConnectionRequestTimeout(timeout * 1000)
+ .setSocketTimeout(timeout * 1000).build();
+
+ HttpClientBuilder b = HttpClientBuilder.create()
+ .setConnectionManager(new BasicHttpClientConnectionManager(socketFactoryRegistry))
+ .setMaxConnPerRoute(20)
+ .setMaxConnTotal(400)
+ .setDefaultRequestConfig(config);
+
+
+ client = b.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void post(String url, Map paramMap, Map headerMap) throws IOException {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ /*
+ * 处理参数
+ */
+ List params = new ArrayList();
+ if (MapUtils.isNotEmpty(paramMap)) {
+ Set keySet = paramMap.keySet();
+ for (String key : keySet) {
+ params.add(new BasicNameValuePair(key, paramMap.get(key)));
+ }
+ }
+
+ /*
+ * 设置头信息
+ */
+ if (MapUtils.isNotEmpty(headerMap)) {
+ Set keySet = headerMap.keySet();
+ for (String key : keySet) {
+ httpPost.addHeader(key, headerMap.get(key));
+ }
+ }
+
+ httpPost.setEntity(new UrlEncodedFormEntity(params, ENCODING));
+
+ HttpResponse response = client.execute(httpPost);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reponse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ // 文件保存位置
+ File saveDir = new File(".");
+ if (!saveDir.exists()) {
+ saveDir.mkdirs();
+ }
+
+ //输出
+ File file = new File(saveDir + File.separator + "testssl.doc");
+ FileOutputStream fos = new FileOutputStream(file);
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ fos.write(b, 0, len);
+ }
+ fos.close();
+
+ inputStream.close();
+ httpPost.abort();
+ }
+
+ public static void get(String url) throws IOException {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ HttpResponse response = client.execute(httpGet);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reppnse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ System.out.print(new String(b, 0, len));
+ }
+
+ httpGet.abort();
+ }
+
+ public static void main(String[] args) {
+ try {
+ //初始化
+ initGMSSL();
+
+ // 测试GET
+ String url;
+ // url = "https://ebssec.boc.cn/";
+ url = "https://localhost/echo";
+ // url = "https://localhost/a";
+ // url = "https://www.baidu.com/";
+ HttpClient1.get(url);
+
+ HttpClient1.get(url);
+
+
+ // HttpClient1.post();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+
diff --git a/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient2.java b/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient2.java
new file mode 100644
index 0000000..e435f1c
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient2.java
@@ -0,0 +1,336 @@
+package cc.kkon.gmssl_cn.httpclient;
+
+import org.apache.commons.collections4.MapUtils;
+import org.apache.http.*;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.apache.http.message.BasicNameValuePair;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 使用HttpClient访问标准https和国密https
+ *
+ * @author gmssl.cn
+ */
+public class HttpClient2 {
+ private static final String ENCODING = "UTF-8";
+
+ private static HttpClient client4GM = null;
+ private static HttpClient client4Std = null;
+
+ // 创建SSL上下文---忽略服务端证书信任
+ static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException {
+ SSLContext sc = SSLContext.getInstance(cn.gmssl.jsse.provider.GMJSSE.GMSSLv11, cn.gmssl.jsse.provider.GMJSSE.NAME);
+
+ // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
+ X509TrustManager trustManager = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ for (int i = 0; i < paramArrayOfX509Certificate.length; i++) {
+ System.out.println(paramArrayOfX509Certificate[i].getSubjectDN().getName());
+ }
+ System.out.println();
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ for (int i = 0; i < paramArrayOfX509Certificate.length; i++) {
+ System.out.println(paramArrayOfX509Certificate[i].getSubjectDN().getName());
+ }
+ System.out.println();
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+ sc.init(null, new TrustManager[]{trustManager}, null);
+ return sc;
+ }
+
+
+ static SSLContext createIgnoreVerifySSL4Std() throws NoSuchAlgorithmException, KeyManagementException {
+ SSLContext sc = SSLContext.getInstance("TLSv1.2");
+
+ // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
+ X509TrustManager trustManager = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+ sc.init(null, new TrustManager[]{trustManager}, null);
+ return sc;
+ }
+
+ private static void initGMSSL() {
+ try {
+ Security.insertProviderAt(new cn.gmssl.jce.provider.GMJCE(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ SSLContext sslContext = createIgnoreVerifySSL();
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
+ new String[]{"GMSSLv1.1"}, new String[]{"ECC_SM4_CBC_SM3", "ECC_SM4_GCM_SM3"},
+ SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ Registry socketFactoryRegistry =
+ RegistryBuilder.create()
+ .register("https", sslsf).build();
+
+ int timeout = 5;
+ RequestConfig config = RequestConfig.custom()
+ .setConnectTimeout(timeout * 1000)
+ .setConnectionRequestTimeout(timeout * 1000)
+ .setSocketTimeout(timeout * 1000).build();
+
+ HttpClientBuilder b = HttpClientBuilder.create()
+ .setConnectionManager(new BasicHttpClientConnectionManager(socketFactoryRegistry))
+ .setMaxConnPerRoute(20)
+ .setMaxConnTotal(400)
+ .setDefaultRequestConfig(config);
+
+
+ client4GM = b.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void initStandard() {
+ try {
+ SSLContext sslContext = createIgnoreVerifySSL4Std();
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
+ SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ Registry socketFactoryRegistry =
+ RegistryBuilder.create()
+ .register("https", sslsf).build();
+
+ int timeout = 5;
+ RequestConfig config = RequestConfig.custom()
+ .setConnectTimeout(timeout * 1000)
+ .setConnectionRequestTimeout(timeout * 1000)
+ .setSocketTimeout(timeout * 1000).build();
+
+ HttpClientBuilder b = HttpClientBuilder.create()
+ .setConnectionManager(new BasicHttpClientConnectionManager(socketFactoryRegistry))
+ .setMaxConnPerRoute(20)
+ .setMaxConnTotal(400)
+ .setDefaultRequestConfig(config);
+
+
+ client4Std = b.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void post(String url, Map paramMap, Map headerMap) throws IOException {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ /*
+ * 处理参数
+ */
+ List params = new ArrayList();
+ if (MapUtils.isNotEmpty(paramMap)) {
+ Set keySet = paramMap.keySet();
+ for (String key : keySet) {
+ params.add(new BasicNameValuePair(key, paramMap.get(key)));
+ }
+ }
+
+ /*
+ * 设置头信息
+ */
+ if (MapUtils.isNotEmpty(headerMap)) {
+ Set keySet = headerMap.keySet();
+ for (String key : keySet) {
+ httpPost.addHeader(key, headerMap.get(key));
+ }
+ }
+
+ httpPost.setEntity(new UrlEncodedFormEntity(params, ENCODING));
+
+ HttpResponse response = client4GM.execute(httpPost);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reponse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ // 文件保存位置
+ File saveDir = new File(".");
+ if (!saveDir.exists()) {
+ saveDir.mkdirs();
+ }
+
+ //输出
+ File file = new File(saveDir + File.separator + "testssl.doc");
+ FileOutputStream fos = new FileOutputStream(file);
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ fos.write(b, 0, len);
+ }
+ fos.close();
+
+ inputStream.close();
+ httpPost.abort();
+ }
+
+ public static void post4Std(String url, Map paramMap, Map headerMap) throws IOException {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ /*
+ * 处理参数
+ */
+ List params = new ArrayList();
+ if (MapUtils.isNotEmpty(paramMap)) {
+ Set keySet = paramMap.keySet();
+ for (String key : keySet) {
+ params.add(new BasicNameValuePair(key, paramMap.get(key)));
+ }
+ }
+
+ /*
+ * 设置头信息
+ */
+ if (MapUtils.isNotEmpty(headerMap)) {
+ Set keySet = headerMap.keySet();
+ for (String key : keySet) {
+ httpPost.addHeader(key, headerMap.get(key));
+ }
+ }
+
+ httpPost.setEntity(new UrlEncodedFormEntity(params, ENCODING));
+
+ HttpResponse response = client4Std.execute(httpPost);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reponse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ // 文件保存位置
+ File saveDir = new File(".");
+ if (!saveDir.exists()) {
+ saveDir.mkdirs();
+ }
+
+ //输出
+ File file = new File(saveDir + File.separator + "testssl.doc");
+ FileOutputStream fos = new FileOutputStream(file);
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ fos.write(b, 0, len);
+ }
+ fos.close();
+
+ inputStream.close();
+ httpPost.abort();
+ }
+
+ public static void get(String url) throws IOException {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ HttpResponse response = client4GM.execute(httpGet);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reppnse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ System.out.print(new String(b, 0, len));
+ }
+
+ httpGet.abort();
+ }
+
+ public static void get4Std(String url) throws IOException {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ HttpResponse response = client4Std.execute(httpGet);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reppnse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ System.out.print(new String(b, 0, len));
+ }
+
+ httpGet.abort();
+ }
+
+ public static void main(String[] args) {
+ try {
+ //初始化
+ initGMSSL();
+ initStandard();
+
+ // 测试GET
+ String url = "https://demo.gmssl.cn:444/";
+ HttpClient2.get(url);
+
+ // 测试GET
+ url = "https://www.baidu.com/";
+ HttpClient2.get4Std(url);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+
+
diff --git a/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient3.java b/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient3.java
new file mode 100644
index 0000000..ac87907
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient3.java
@@ -0,0 +1,207 @@
+package cc.kkon.gmssl_cn.httpclient;
+
+import org.apache.commons.collections4.MapUtils;
+import org.apache.http.*;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.apache.http.message.BasicNameValuePair;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 使用HttpClient访问国密https(双向)
+ *
+ * @author gmssl.cn
+ */
+public class HttpClient3 {
+ private static final String ENCODING = "UTF-8";
+ private static HttpClient client = null;
+
+
+ // 创建SSL上下文---忽略服务端证书信任
+ static SSLContext createIgnoreVerifySSL(KeyStore keypair, String pwd) throws Exception {
+ SSLContext sc = SSLContext.getInstance(cn.gmssl.jsse.provider.GMJSSE.GMSSLv11, cn.gmssl.jsse.provider.GMJSSE.NAME);
+
+ // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
+ X509TrustManager trustManager = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ for (int i = 0; i < paramArrayOfX509Certificate.length; i++) {
+ System.out.println(paramArrayOfX509Certificate[i].getSubjectDN().getName());
+ }
+ System.out.println();
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
+ for (int i = 0; i < paramArrayOfX509Certificate.length; i++) {
+ System.out.println(paramArrayOfX509Certificate[i].getSubjectDN().getName());
+ }
+ System.out.println();
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+
+ KeyManager[] kms = null;
+ if (keypair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(keypair, pwd.toCharArray());
+ kms = kmf.getKeyManagers();
+ }
+ sc.init(kms, new TrustManager[]{trustManager}, null);
+ return sc;
+ }
+
+ private static void initGMSSL() {
+ try {
+ Security.insertProviderAt(new cn.gmssl.jce.provider.GMJCE(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ String pfxfile = "keystore/sm2.user1.both.pfx";
+ String pwd = "12345678";
+ KeyStore pfx = KeyStore.getInstance("PKCS12", "GMJCE");
+ pfx.load(new FileInputStream(pfxfile), pwd.toCharArray());
+
+ SSLContext sslContext = createIgnoreVerifySSL(pfx, pwd);
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
+ new String[]{"GMSSLv1.1"}, new String[]{"ECC_SM4_CBC_SM3"},
+ SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ Registry socketFactoryRegistry =
+ RegistryBuilder.create()
+ .register("https", sslsf).build();
+
+ int timeout = 5;
+ RequestConfig config = RequestConfig.custom()
+ .setConnectTimeout(timeout * 1000)
+ .setConnectionRequestTimeout(timeout * 1000)
+ .setSocketTimeout(timeout * 1000).build();
+
+ HttpClientBuilder b = HttpClientBuilder.create()
+ .setConnectionManager(new BasicHttpClientConnectionManager(socketFactoryRegistry))
+ .setMaxConnPerRoute(20)
+ .setMaxConnTotal(400)
+ .setDefaultRequestConfig(config);
+
+
+ client = b.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void post(String url, Map paramMap, Map headerMap) throws IOException {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ /*
+ * 处理参数
+ */
+ List params = new ArrayList();
+ if (MapUtils.isNotEmpty(paramMap)) {
+ Set keySet = paramMap.keySet();
+ for (String key : keySet) {
+ params.add(new BasicNameValuePair(key, paramMap.get(key)));
+ }
+ }
+
+ /*
+ * 设置头信息
+ */
+ if (MapUtils.isNotEmpty(headerMap)) {
+ Set keySet = headerMap.keySet();
+ for (String key : keySet) {
+ httpPost.addHeader(key, headerMap.get(key));
+ }
+ }
+
+ httpPost.setEntity(new UrlEncodedFormEntity(params, ENCODING));
+
+ HttpResponse response = client.execute(httpPost);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reponse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ // 文件保存位置
+ File saveDir = new File(".");
+ if (!saveDir.exists()) {
+ saveDir.mkdirs();
+ }
+
+ //输出
+ File file = new File(saveDir + File.separator + "testssl.doc");
+ FileOutputStream fos = new FileOutputStream(file);
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ fos.write(b, 0, len);
+ }
+ fos.close();
+
+ inputStream.close();
+ httpPost.abort();
+ }
+
+ public static void get(String url) throws IOException {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.setProtocolVersion(HttpVersion.HTTP_1_1);
+
+ HttpResponse response = client.execute(httpGet);
+
+ StatusLine status = response.getStatusLine();
+ System.out.println("Reppnse status=" + status.getStatusCode());
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ byte[] b = new byte[1024];
+ int len = 0;
+ while ((len = inputStream.read(b)) != -1) {
+ System.out.print(new String(b, 0, len));
+ }
+
+ httpGet.abort();
+ }
+
+ public static void main(String[] args) {
+ try {
+ //初始化
+ initGMSSL();
+
+ // 测试GET
+ String url = "https://demo.gmssl.cn:1443/";
+ HttpClient3.get(url);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+
diff --git a/src/test/java/cc/kkon/gmssl_cn/server/Server1.java b/src/test/java/cc/kkon/gmssl_cn/server/Server1.java
new file mode 100644
index 0000000..38832d7
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/server/Server1.java
@@ -0,0 +1,186 @@
+package cc.kkon.gmssl_cn.server;
+
+
+import cc.kkon.gmssl_cn.client.TrustAllManager;
+
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+
+/**
+ * 单向认证
+ *
+ * @author gmssl.cn
+ */
+public class Server1 {
+ public Server1() {
+ }
+
+ public static void main(String[] args) throws Exception {
+ ServerSocketFactory fact = null;
+ SSLServerSocket serversocket = null;
+
+ System.out.println("Usage: java -cp GMExample.jar server.Server1 port");
+ int port = 443;
+ if (args.length > 0) {
+ port = Integer.parseInt(args[0]);
+ }
+
+ System.out.println("Port=" + port);
+
+ String pfxfile = "keystore/sm2.server1.both.pfx";
+ String pwdpwd = "12345678";
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ KeyStore pfx = KeyStore.getInstance("PKCS12", "GMJSSE");
+ pfx.load(new FileInputStream(pfxfile), pwdpwd.toCharArray());
+
+ fact = createServerSocketFactory(pfx, pwdpwd.toCharArray());
+ serversocket = (SSLServerSocket) fact.createServerSocket(port);
+
+ System.out.println("listening...");
+
+ while (true) {
+ Socket socket = null;
+ try {
+ socket = serversocket.accept();
+ System.out.println("client comes");
+
+ DataInputStream in = new DataInputStream(socket.getInputStream());
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+ boolean get = false;
+ while (true) {
+ byte[] lineBuf = ReadLine.read(in);
+ if (lineBuf == null || lineBuf.length == 0) {
+ break;
+ }
+ String line = new String(lineBuf);
+ System.out.println(line);
+ if (!get)
+ get = line.startsWith("GET ");
+ }
+
+ if (!get) {
+ byte[] buf = new byte[8192];
+ int len = in.read(buf);
+ System.out.println(new String(buf, 0, len));
+ }
+
+ byte[] body = "this is a gm server".getBytes();
+ byte[] resp = ("HTTP/1.1 200 OK\r\nServer: GMSSL/1.0\r\nContent-Length:" + body.length + "\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n").getBytes();
+ out.write(resp, 0, resp.length);
+ out.write(body, 0, body.length);
+ out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+
+ public static SSLServerSocketFactory createServerSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
+ TrustManager[] trust = {new TrustAllManager()};
+
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ java.security.SecureRandom secureRandom = new java.security.SecureRandom();
+ ctx.init(kms, trust, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLServerSocketFactory factory = ctx.getServerSocketFactory();
+ return factory;
+ }
+}
+
+class ReadLine {
+ public static final byte[] CRLF = {'\r', '\n'};
+ public static final byte CR = '\r';
+ public static final byte LF = '\n';
+
+ private static final int LINE_MAX_SIZE = 16384;
+
+ public static byte[] read(DataInputStream in) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream s = new DataOutputStream(baos);
+ boolean previousIsCR = false;
+
+ int len = 0;
+ byte b = 0;
+
+ try {
+ b = in.readByte();
+ len++;
+ } catch (EOFException e) {
+ //2022.01.06
+ //return new byte[0];
+ return null;
+ }
+
+ while (true) {
+ if (b == LF) {
+ if (previousIsCR) {
+ s.flush();
+ byte[] rs = baos.toByteArray();
+ s.close();
+ return rs;
+ } else {
+ /**
+ * 因为测试到java.sun.com网站,返回HTTP头的行结束符是"\n",而不是FRC中规定的"\r\n"。
+ * IE可以正确解释,故修正为行结束判断为"\n"。
+ */
+ //s.write(b);
+
+ s.flush();
+ byte[] rs = baos.toByteArray();
+ s.close();
+ return rs;
+ }
+ } else if (b == CR) {
+ if (previousIsCR) {
+ s.writeByte(CR);
+ }
+ previousIsCR = true;
+ } else {
+ if (previousIsCR) {
+ s.writeByte(CR);
+ }
+ previousIsCR = false;
+ s.write(b);
+ }
+
+ if (len > LINE_MAX_SIZE) {
+ s.close();
+ throw new IOException("Reach line size limit");
+ }
+
+ try {
+ b = in.readByte();
+ len++;
+ } catch (EOFException e) {
+ s.flush();
+ byte[] rs = baos.toByteArray();
+ s.close();
+ return rs;
+ }
+ }
+ }
+}
diff --git a/src/test/java/cc/kkon/gmssl_cn/server/Server2.java b/src/test/java/cc/kkon/gmssl_cn/server/Server2.java
new file mode 100644
index 0000000..56bda93
--- /dev/null
+++ b/src/test/java/cc/kkon/gmssl_cn/server/Server2.java
@@ -0,0 +1,105 @@
+package cc.kkon.gmssl_cn.server;
+
+
+import cc.kkon.gmssl_cn.client.TrustAllManager;
+
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.*;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+
+/**
+ * 双向认证
+ *
+ * @author gmssl.cn
+ */
+public class Server2 {
+ public Server2() {
+ }
+
+ public static void main(String[] args) throws Exception {
+ ServerSocketFactory fact = null;
+ SSLServerSocket serversocket = null;
+
+ System.out.println("Usage: java -cp GMExample.jar server.Server2 port");
+ int port = 8444;
+ if (args.length > 0) {
+ port = Integer.parseInt(args[0]);
+ }
+
+ String pfxfile = "keystore/sm2.server1.both.pfx";
+ String pwdpwd = "12345678";
+
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jce.provider.GMJCE").newInstance(), 1);
+ Security.insertProviderAt((Provider) Class.forName("cn.gmssl.jsse.provider.GMJSSE").newInstance(), 2);
+
+ KeyStore pfx = KeyStore.getInstance("PKCS12", "GMJSSE");
+ pfx.load(new FileInputStream(pfxfile), pwdpwd.toCharArray());
+
+ fact = createServerSocketFactory(pfx, pwdpwd.toCharArray());
+ serversocket = (SSLServerSocket) fact.createServerSocket(port);
+ serversocket.setNeedClientAuth(true);
+
+ while (true) {
+ SSLSocket socket = null;
+ try {
+ socket = (SSLSocket) serversocket.accept();
+
+ DataInputStream in = new DataInputStream(socket.getInputStream());
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+ byte[] buf = new byte[8192];
+ int len = in.read(buf);
+ if (len == -1) {
+ System.out.println("eof");
+ }
+ System.out.println(new String(buf, 0, len));
+
+ byte[] body = "this is a gm server".getBytes();
+ byte[] resp = ("HTTP/1.1 200 OK\r\nServer: GMSSL/1.0\r\nContent-Length:" + body.length + "\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n").getBytes();
+ out.write(resp, 0, resp.length);
+ out.write(body, 0, body.length);
+ out.flush();
+
+ javax.security.cert.X509Certificate[] cs = socket.getSession().getPeerCertificateChain();
+ System.out.println("client certs len=" + cs.length);
+ for (int i = 0; i < cs.length; i++) {
+ System.out.println(cs[i]);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+
+ public static SSLServerSocketFactory createServerSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
+ TrustManager[] trust = {new TrustAllManager()};
+
+ KeyManager[] kms = null;
+ if (kepair != null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(kepair, pwd);
+ kms = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("GMSSLv1.1", "GMJSSE");
+ java.security.SecureRandom secureRandom = new java.security.SecureRandom();
+ ctx.init(kms, trust, secureRandom);
+
+ ctx.getServerSessionContext().setSessionCacheSize(8192);
+ ctx.getServerSessionContext().setSessionTimeout(3600);
+
+ SSLServerSocketFactory factory = ctx.getServerSocketFactory();
+ return factory;
+ }
+}
+
diff --git a/src/test/resources/certs/sm2.auth1.both.pfx b/src/test/resources/certs/sm2.auth1.both.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..53dc462045961567c7a304a351b289b5da2400ca
GIT binary patch
literal 3038
zcmdVc3sh5e90&0G+IURIWCI0(z*Q)u{O{gVBw>gkAP8Y1Vr{#-h)k$4LGT=vi54nK
zsU!wEUO`eSprMx1K?Typ5Haw9CYZbw4Mhc#?vzxJ&dKtePCNUbo%{Xo_51xl=eGeJ
z;{pPsK*!M2O`O(wue(1QHiGp!#wVDLkxIoW)LJ`qD~{UIP;oTSA)a)`;HyTN!l+&c
zw*eh|oo+f9npjfr1=nLbxR%OKQ|kjgiogg$hcyKtR|7`Tjh&Kg&V;~gmeC;KgEN2$
z9Sy=1yFD4B
zv42Mus|6FrnehNF#Cd!i7Xj+Fz!2~a0T>Kk<;R5rHrB@ps)9-tg4hB-ovs1P0IsGD-sMH%X#
z*5>CNRqh#D+4|xEKeRuYS?eGiDE0h3t3S%2#|b?_|u6;no8$%gh@BGZB07`Nq42U-gdnvbPpy
zb=+g{opuFGWptC%%zf{u+u!Y{l<89xGuk>LT+~I){SB8I&)T)HSB_ddr)5hv*GZBs
z7muVYU_@mKg;FRXx@qvgS`J_lJ7Rco)Qpr~eNCgGTW0*4d1|_L^n6f%b*%>N7
zMXi`Wl_e)k3R^Qw*(*Kv$If^>9LELE%`3z7dKv)R5uKrIntYjdF-{%sU(KM5rPFpE
z61Q~3Rki2H{df-i&cf0NtKPL;2zSpZqI$&_!7|D6oTKAwTQeO!{9QinyE2cFUOTH8
zP38;N?kwoZjR2}4a$ex^Oity`suhl5bu)U%__vg|_QnF1LEIS31Q_WCzo=ov+p;kP
zK*NUB_d`33K33!a9b8@XMr>2V!%YjdX5smA|I%~a4e#BILaiJWwfi>so<;0!cel)y
zRoJGw^zU>ujiWb}c`w?bZ>(LTiq
z{k)e^BpKS_CyMM<6K0P-I5r`tK-M$gGkW$B^{Oe|>zZmqxl7UxS`2V6QnM)MdS8(=
zFFaIMF8JQ-{YNZlAj=`twX5FL?;HO+ffj)yDVhmIL&_HvDa8a3iWMTB&;SPrBx0qK
z`qcvjPb4J?f>H!qK0k{PebRRz=@77aI*UfZbil-nEsnWe^}Oaa9YHV%OWN+Wu<4%0
zzq*R2&A(E!F|GfAU3?icm6+sEBi&?o&D<}!G%b`lYt43X-o6X*j$3V=HWts#I5J~W
zcE$9H)5N)YZQ|)9PC?Bv*L^30K6FpWnGo!zcJJT6=s^B0>lE6`gQxxXsh7Zt9UBJ@&aer##vIHT78TkDes)R`q|-nZ&x=
zll93F>jyg%4ROA#QXF#LWg}rm;$v&)TJ&bl>=0*g{Md^0iRd)=+tz`p69dY6=XFRw
z-t9|>w*0Iai3-=gS&|H$A?OEk^v%e*t{?!;6`h!Y+czwIaftqbqgyd0MmOR?|&C1Wd#*Z}%
z&(nX38mTk?aPr{5f%
literal 0
HcmV?d00001
diff --git a/src/test/resources/certs/sm2.auth1.enc.pfx b/src/test/resources/certs/sm2.auth1.enc.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..e61724f643fde0c2fa8268dc254268311df4e71f
GIT binary patch
literal 976
zcmXqLVm`yf$ZXKWJc*4{tIebBJ1-+UMni!uOG%-GAEy*x6h%^vpV+VTWoGlY0%%M%X%uWm}E7yNk6v(mMbx6kSpWO1Ipx}y|
zp^v;`e>4OwT%z`JbJ6=36GW0KC)heYDe2$ceZoZGRm0KvIG%oCfSoP=|s7O;(tN$$$YIM6hjQ?3!n3)(XP(zs6gTcU+Ng-9qi+6{3
z<)Y=z-y&S{y!PhQ>wRv!cQH~qYr+oE|H`+S6d4vh{k__9No=I^ev<=k3I7Bb{?Ap6
zw0VD(%V(SK#{kg)Lk|N{c(ULWG31bAV{m22V@NU3M+l1=DzPY7n46lKrx_ZWnwusY
zB&L{Kq?)7}StKTpDxW}Mzr-2MS74a~#8St@id`~zUzVpkq
zjZ91|3@nW^Rwgz1pA0zcQLud1&4&DS+uC$IYm#>*?c6+BH)$^0xo7u{
zU1?mEpZ>1)SVKlkvhNz%w{lnZ`po<)slhqr>LA#_h5fAA(zo)smzr6iA>EOHS=sW+TZq+L+oA-fHaqGU;aAD@c`!a_<
zXMni!uOG%-GAy8}P7k
zXf?{54`pmFWo0n%Hsm(oWMd9xVH0L@_A}%IN&`7u!mRGT!NER;0tS2_KDRJ?fVZc9
zu&Ey*x6h%^vpV+VTWoGlY0%%M%X%uWm}Gjpxy?KFS#y6pI?MTtH}ipFnN
zY)+T_)%u=EdEM!h6V=Otoh>RB?UTOvVvf3#ul}Up_rtiSU3A~b++u9nI&b;6#pwpA
z2GT&c%PO;o7zi~8{Hzb%qCUkqX<68*{4=pq{|zKTl6)*eECR=_r&g;+Uv~JGR<4u$
zZhf(V07y!Zh0}n&3F=T#pveleFc~m_gGiR2k?}tZ3o{eL0n`v?_Fyn@Wm06=(Xd*z
z_}-15eK95cu}c$uH(&XDBmQkRv!r6u#SPuwmzWe}S3L=IJ|x?#|7V^$R~g&A?H+Nn
z7TmnpCH!EukT~&MB&MTQ^b%%j*Y>UA&()&Kp!D2YN*7bU}0`*YMy3jXlibn
zY>=2@Zjow|YGjd^l$w%kXq;r03QQDY$f<~FLE|2S#+?Q-@KnUZ$Y#LD!ohxO&2wYD
zQ{qfaEDS7-Gp@aupBTy_{?cHrT+l9;O!cgo8pe9*9crvgoSHoro($*LjExTe&%g5-
zOMsEQUEdo9zq*7=au?P|9ueXz+?3C`&5gS#CP}NWRKP5F+C<*39(uDsaOvI+lYJGG
zaYs;jgLt2j6NkWt`Y>g=X7QtpNqPZK_A(hX$u3+aKGWdM&)0GWvnta(TRZ#pC56+q
z)#tUF2TWAU5&urbkTEn+1m*`;Z9ZluDOLs+5f#7V5pGlUT4sNZIuh_Taaqp&BX3za
PR+_LbEH``m2ox>=oCzkZ
literal 0
HcmV?d00001
diff --git a/src/test/resources/certs/sm2.oca.pem b/src/test/resources/certs/sm2.oca.pem
new file mode 100644
index 0000000..135628f
--- /dev/null
+++ b/src/test/resources/certs/sm2.oca.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB6zCCAY+gAwIBAgIGAXKnMMauMAwGCCqBHM9VAYN1BQAwSTELMAkGA1UEBhMC
+Q04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRgwFgYDVQQDEw9S
+b290Q0EgZm9yIFRlc3QwIhgPMjAxNTEyMzExNjAwMDBaGA8yMDM1MTIzMDE2MDAw
+MFowSzELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kv
+U00yMRowGAYDVQQDExFNaWRkbGVDQSBmb3IgVGVzdDBZMBMGByqGSM49AgEGCCqB
+HM9VAYItA0IABA4uB1fiqJjs1uR6bFIrtxvLFuoU0x+uPPxrslzodyTG1Mj9dJpm
+4AUjT9q2bL4cj7H73qWJNpwArnZr7fCc3A2jWzBZMBsGA1UdIwQUMBKAEJxp7A+6
+GjnFr+gk67KcEgQwGQYDVR0OBBIEEPl/VbQnlDNiplbKb8xdGv8wDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAMYwDAYIKoEcz1UBg3UFAANIADBFAiA31tn0
+qKz6G0YgGjWd6/ULMyqfTzoL82Y7EkvxbOpX/AIhAKCJYkDp62cvbKvj/Njc2dIe
+5BN+DGhO5JOhIyo4oWE3
+-----END CERTIFICATE-----
diff --git a/src/test/resources/certs/sm2.rca.pem b/src/test/resources/certs/sm2.rca.pem
new file mode 100644
index 0000000..b1d30c8
--- /dev/null
+++ b/src/test/resources/certs/sm2.rca.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBzTCCAXCgAwIBAgIGAXKnMKNyMAwGCCqBHM9VAYN1BQAwSTELMAkGA1UEBhMC
+Q04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRgwFgYDVQQDEw9S
+b290Q0EgZm9yIFRlc3QwIhgPMjAxNTEyMzExNjAwMDBaGA8yMDM1MTIzMDE2MDAw
+MFowSTELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kv
+U00yMRgwFgYDVQQDEw9Sb290Q0EgZm9yIFRlc3QwWTATBgcqhkjOPQIBBggqgRzP
+VQGCLQNCAATj+apYlL+ddWXZ7+mFZXZJGbcJFXUN+Fszz6humeyWZP4qEEr2N0+a
+Zdwo/21ft232yo0jPLzdscKB261zSQXSoz4wPDAZBgNVHQ4EEgQQnGnsD7oaOcWv
+6CTrspwSBDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIAxjAMBggqgRzP
+VQGDdQUAA0kAMEYCIQCEnW5BlQh0vmsOLxSoXYc/7zs++wWyFc1tnBHENR4ElwIh
+AI1Lwu6in1ruflZhzseWulXwcITf3bm/Y5X1g1XFWQUH
+-----END CERTIFICATE-----
diff --git a/src/test/resources/keystore/sm2.oca.pem b/src/test/resources/keystore/sm2.oca.pem
new file mode 100644
index 0000000..135628f
--- /dev/null
+++ b/src/test/resources/keystore/sm2.oca.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB6zCCAY+gAwIBAgIGAXKnMMauMAwGCCqBHM9VAYN1BQAwSTELMAkGA1UEBhMC
+Q04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRgwFgYDVQQDEw9S
+b290Q0EgZm9yIFRlc3QwIhgPMjAxNTEyMzExNjAwMDBaGA8yMDM1MTIzMDE2MDAw
+MFowSzELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kv
+U00yMRowGAYDVQQDExFNaWRkbGVDQSBmb3IgVGVzdDBZMBMGByqGSM49AgEGCCqB
+HM9VAYItA0IABA4uB1fiqJjs1uR6bFIrtxvLFuoU0x+uPPxrslzodyTG1Mj9dJpm
+4AUjT9q2bL4cj7H73qWJNpwArnZr7fCc3A2jWzBZMBsGA1UdIwQUMBKAEJxp7A+6
+GjnFr+gk67KcEgQwGQYDVR0OBBIEEPl/VbQnlDNiplbKb8xdGv8wDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAMYwDAYIKoEcz1UBg3UFAANIADBFAiA31tn0
+qKz6G0YgGjWd6/ULMyqfTzoL82Y7EkvxbOpX/AIhAKCJYkDp62cvbKvj/Njc2dIe
+5BN+DGhO5JOhIyo4oWE3
+-----END CERTIFICATE-----
diff --git a/src/test/resources/keystore/sm2.rca.pem b/src/test/resources/keystore/sm2.rca.pem
new file mode 100644
index 0000000..b1d30c8
--- /dev/null
+++ b/src/test/resources/keystore/sm2.rca.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBzTCCAXCgAwIBAgIGAXKnMKNyMAwGCCqBHM9VAYN1BQAwSTELMAkGA1UEBhMC
+Q04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRgwFgYDVQQDEw9S
+b290Q0EgZm9yIFRlc3QwIhgPMjAxNTEyMzExNjAwMDBaGA8yMDM1MTIzMDE2MDAw
+MFowSTELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kv
+U00yMRgwFgYDVQQDEw9Sb290Q0EgZm9yIFRlc3QwWTATBgcqhkjOPQIBBggqgRzP
+VQGCLQNCAATj+apYlL+ddWXZ7+mFZXZJGbcJFXUN+Fszz6humeyWZP4qEEr2N0+a
+Zdwo/21ft232yo0jPLzdscKB261zSQXSoz4wPDAZBgNVHQ4EEgQQnGnsD7oaOcWv
+6CTrspwSBDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIAxjAMBggqgRzP
+VQGDdQUAA0kAMEYCIQCEnW5BlQh0vmsOLxSoXYc/7zs++wWyFc1tnBHENR4ElwIh
+AI1Lwu6in1ruflZhzseWulXwcITf3bm/Y5X1g1XFWQUH
+-----END CERTIFICATE-----
diff --git a/src/test/resources/keystore/sm2.server1.both.pfx b/src/test/resources/keystore/sm2.server1.both.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..5154b4aabc7cf1d5d681279acaffd075fc6f7d30
GIT binary patch
literal 3030
zcmdVc3s6*57zgnC+FfvQ5f+T6u0mR%a_+v@6cj{76pRfF0d=3W@{AQ!Qj$ecnqjbn
z0s=)rTS!qJNh*Rl3dsg4A}WHGkjYy!G!?V6g$gUqWaCWJ&Ys!1|8w`=J>U7wcNb{r
zjTnjp4Ly@;?3BDP`TkgxhURJLDTIc;1;&YRY#cci38z-WSP3-PJSu(YTP9;rJWqpm
z0uA~zb<9v`JRV*Py+~-#^DwW6BRUVqPzyIYIQV`%p(t$A*
z_a~@K%)^Idz-f3ehDlklL@ZuRjspz+F`Y?U>h0wsUcx4=z<7OvGfn-XqSkoKWhtX#
zSnny+YrteHQ#K%ZB%4E$d;nj|^#Mm8fT7@Z-YyU@sWiCghS?bUbV-`&xI03$KPY-X
zy*f8maiza=iz3d;y298pcI@v#oU_`ja>bWh-$t!RDgNyN&Eh6XSAJJ*G!(M5hnla_lb6F(-hjeu{~rPY4RY;m2P!
zvNzQS1dbs!BTI~XAz(f>3E7+*IXA;-O;s4f#X=jh)vf;>*GH66%le2Mvs1hY#24_C
zRPU30dv-1FO9+rO)P8={|914|>upD>Wf=oW{-*;eG;$%ZdCehyL`7uI__r*&+85C{ae
zdM*xx1u)q-4{?7^+SlwlUHT75CNVTOVE7fH|_q1w)0swyxi^q
zI>%|3?@an#b
zaL;^T7Gf1ql&=DzBka5Y(%Y(bTWlQ1eIbroL?#WBM-TXM~JRSz49qu!Fr(lv#QnV>za}
z%~G*1ec8LWKRKcO_WT)lJ=XtPvhV7YiE|uW&h6Pd8YD5ILPB3?R{|gr0S*8Xp$xuB
ziB!lX6L{IN_m+>#vn7N{wrfUESdU^wa@>u>-5aZx6%_nx-BjCn*H24EXI(x1sO&?h
z*~?!A(iCJ%Qrjhmq`FUK#Z_n1lV1&zaAcLAKKW{E9KV`)eQmAP$?}`wZo25LlB_7(
zqW9v0jOyAF6Vy$xC*9hSv*YmU*(;_Vd-wh!k8eqyW<_zQc}9QaHdR}Cen7@(lsu70
zMD_wDnLrMB0x6%()9V3Tp+F{sPd^}M^F>Oz98y9V!zp2TiRB=%1lT>JWP(xwgD}dd
z2t9DEQ1J_f5eSjE{g}?jdwpMXsCIgKC)sd%W{XMF!-k;a(>)__?tu8rmzT?<9c8NE
zj#cw2W1GiV-f6%K2es-{zcwP?$NlkzQ`uAY$<`G*{StigoYXsVf+w?JAgwH35@+?z
zIKcy~FZE!W8!etgE%Q0IQQ=+k_|0h@>nw5XOp|>=d)m?idmzEPvNJYoRmO
z?RY!amKlbo9=6|x2T58Bx@L4OS{`ie&de!X)H=
z?Am;rVz7#jWAw-}ZvC0~c$
z1Uj^dX8A0%7z}?4J&WtmQ!qaU*MfWshGG~E)ocX@4PemBJ++P}WN2y<6#>Bn9azvP
zGMvW7q>~6H+=dsHZ0gvM@bHO*HDDUA>1?wPCIowjhj0mdFxZ$-*sQR)xI}57i#kr@
z@)4Pk2;A&hTtEm2E{`CD0KTRf1D-Je&w@AkcT)kIW(JEsJ_YcA2-;DBY^Vxwp;P6ZVB@uWvi
zEMtIEFr=z~iz&ef2*O;Em3i|6*PADnMKir^3nD-9Z};)*rk2+h$^DyLG-h;hK~Zx|M~gt;>f2}7hvAn<+>
z+YxTKi$pEvae-PPQ1e7`B~QX5)e<#dB~Xxj0gvDU=tyDW2s48r#l+EF=%^hUYKH5J
zK8~u2`xp5p5B8rpn((@8Il%rY+d&~wDpd^G7w)SiF-Mw3G!|sH)LdO06XsPq^~?h9KognFbnIl8q&UvFiY4yJ-3
zMlSBSEwEYfvZWWyx>97jhp*!)zMFg`?)OMe@9Q!fhk*An7g0^!y@KtJh6K5A1bNN(
z={(Pkq3_Y}s(oxG+>W^w(F2v`XD9-0HqY^n*>wI#)#0(bq(G7OWS~2-aSR|Pqp7@qKPgYmOfSCE
zR<1iRhwtB6EBE^}ZQF%a25F;%^_|<77(R%ZN%!DIR$efoZlL*KqfCqqFtb}hut`PGHk}m{Yv4Z3(ff$-0OwH6l
z7pG0kR6sLjFyCWhW(?84M)3bJ^E>NcPp?16P?qcF;nW#AH!O2j>bfo0)7F*_x1XtB
zvGLn{LCzvwk^F(V&!&S+atmIPd_x!T*k!~}pU|<72lHRY3}ZGgF1zAzSH};dSlcq)
z>t1a}+`pgCA7{OaHFM=(DFw9A-@WC1#&^k=A6+Wo45#Fk&un6BS$Vh)qG__(cyFom
zhyXr*(xb6uWm|6H_;Xo?RJT~09jlsKY6lI3=6z&7qI?OBQltU`kwVB77@YvVSfo_K
zM;cIZg%Y(&1<|0)zG$#~?QWpm1swlI!$4_(iJNy#DeumBSmaIrS`owai*qDGfPu=zh`K8&2Ne^GirWZV^J~a&(%SD
zJpzK5Vf!AgAk)}fmt$sEb((qA0p~9imwB~E2F|NY+}>^I{0}*OA
z<36@bO8(`xAo_8+Q^bN_a?>99T9Vl0>G=~Q-}PI%KPodQg12heH-w|>ry_8Pi%eFFAfZ<-KKlLlT{HioUeCc%fmSj{M8PLI78&
woa)7-U>quf+s?Q)GB#uJxet!;8$N73VdcTz7IF(WPqDThd%du+(%6r`0MV3?O8@`>
literal 0
HcmV?d00001