From 80acca10c4ba3f2ce2745a0aca65902ae1792391 Mon Sep 17 00:00:00 2001 From: sumuzhe <1359581862@qq.com> Date: Mon, 21 Nov 2022 09:38:30 +0800 Subject: [PATCH] init --- .gitignore | 52 +++ README.md | 71 ++- pom.xml | 97 +++++ .../cc/kkon/gmhttps/client/ClientBuilder.java | 94 ++++ .../cc/kkon/gmhttps/client/Response0.java | 49 +++ .../cc/kkon/gmhttps/client/SSLRequests.java | 120 +++++ .../kkon/gmhttps/client/TrustAllManager.java | 22 + .../java/cc/kkon/gmhttps/model/FirstLine.java | 11 + .../gmhttps/server/DefaultHttpServlet.java | 25 ++ .../server/DefaultHttpServletRequest.java | 411 ++++++++++++++++++ .../server/DefaultHttpServletResponse.java | 228 ++++++++++ .../server/DefaultServletOutputStream.java | 52 +++ .../java/cc/kkon/gmhttps/server/ReadLine.java | 77 ++++ .../cc/kkon/gmhttps/server/SSLServer.java | 191 ++++++++ .../cc/kkon/gmhttps/utils/ServletUtils.java | 15 + .../java/cc/kkon/gmhttps/utils/Strings.java | 259 +++++++++++ .../java/cc/kkon/gmhttps/utils/Utils.java | 43 ++ src/test/java/cc/kkon/AppTest.java | 67 +++ src/test/java/cc/kkon/TestServlet1.java | 37 ++ src/test/java/cc/kkon/TestServlet2.java | 41 ++ .../java/cc/kkon/gmssl_cn/client/Client1.java | 102 +++++ .../java/cc/kkon/gmssl_cn/client/Client2.java | 106 +++++ .../java/cc/kkon/gmssl_cn/client/Client3.java | 173 ++++++++ .../java/cc/kkon/gmssl_cn/client/Client4.java | 90 ++++ .../java/cc/kkon/gmssl_cn/client/Client5.java | 131 ++++++ .../kkon/gmssl_cn/client/TrustAllManager.java | 22 + .../kkon/gmssl_cn/httpclient/HttpClient1.java | 207 +++++++++ .../kkon/gmssl_cn/httpclient/HttpClient2.java | 336 ++++++++++++++ .../kkon/gmssl_cn/httpclient/HttpClient3.java | 207 +++++++++ .../java/cc/kkon/gmssl_cn/server/Server1.java | 186 ++++++++ .../java/cc/kkon/gmssl_cn/server/Server2.java | 105 +++++ src/test/resources/certs/sm2.auth1.both.pfx | Bin 0 -> 3038 bytes src/test/resources/certs/sm2.auth1.enc.pfx | Bin 0 -> 976 bytes src/test/resources/certs/sm2.auth1.sig.pfx | Bin 0 -> 976 bytes src/test/resources/certs/sm2.oca.pem | 13 + src/test/resources/certs/sm2.rca.pem | 12 + src/test/resources/keystore/sm2.oca.pem | 13 + src/test/resources/keystore/sm2.rca.pem | 12 + .../resources/keystore/sm2.server1.both.pfx | Bin 0 -> 3030 bytes .../resources/keystore/sm2.user1.both.pfx | Bin 0 -> 2987 bytes 40 files changed, 3676 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/cc/kkon/gmhttps/client/ClientBuilder.java create mode 100644 src/main/java/cc/kkon/gmhttps/client/Response0.java create mode 100644 src/main/java/cc/kkon/gmhttps/client/SSLRequests.java create mode 100644 src/main/java/cc/kkon/gmhttps/client/TrustAllManager.java create mode 100644 src/main/java/cc/kkon/gmhttps/model/FirstLine.java create mode 100644 src/main/java/cc/kkon/gmhttps/server/DefaultHttpServlet.java create mode 100644 src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletRequest.java create mode 100644 src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletResponse.java create mode 100644 src/main/java/cc/kkon/gmhttps/server/DefaultServletOutputStream.java create mode 100644 src/main/java/cc/kkon/gmhttps/server/ReadLine.java create mode 100644 src/main/java/cc/kkon/gmhttps/server/SSLServer.java create mode 100644 src/main/java/cc/kkon/gmhttps/utils/ServletUtils.java create mode 100644 src/main/java/cc/kkon/gmhttps/utils/Strings.java create mode 100644 src/main/java/cc/kkon/gmhttps/utils/Utils.java create mode 100644 src/test/java/cc/kkon/AppTest.java create mode 100644 src/test/java/cc/kkon/TestServlet1.java create mode 100644 src/test/java/cc/kkon/TestServlet2.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/client/Client1.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/client/Client2.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/client/Client3.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/client/Client4.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/client/Client5.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/client/TrustAllManager.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient1.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient2.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/httpclient/HttpClient3.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/server/Server1.java create mode 100644 src/test/java/cc/kkon/gmssl_cn/server/Server2.java create mode 100644 src/test/resources/certs/sm2.auth1.both.pfx create mode 100644 src/test/resources/certs/sm2.auth1.enc.pfx create mode 100644 src/test/resources/certs/sm2.auth1.sig.pfx create mode 100644 src/test/resources/certs/sm2.oca.pem create mode 100644 src/test/resources/certs/sm2.rca.pem create mode 100644 src/test/resources/keystore/sm2.oca.pem create mode 100644 src/test/resources/keystore/sm2.rca.pem create mode 100644 src/test/resources/keystore/sm2.server1.both.pfx create mode 100644 src/test/resources/keystore/sm2.user1.both.pfx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd02f99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +### IntelliJ IDEA ### +.idea +.rider +*.iws +*.iml +*.ipr + +### Visual Studio ### +.vs +*.user + +### C# ### +[Dd]ebug +[Rr]elease +obj/ +[Bb]in +!packages/build/ +Bak/ +packages/ +[Rr]elease/ + +### Python ### +__pycache__ + + +### Maven ### +target + +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock + diff --git a/README.md b/README.md index fe23f21..9065f13 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,71 @@ # gm-https -支持国密 https 的 servlet 容器。 +支持国密 https 的性能 servlet 容器。 + +有限支持 servlet 规范。 + +性能较差,不建议用在正式环境。 + +项目依赖的 gmssl_provider 来自 [https://gmssl.cn/gmssl](https://gmssl.cn/gmssl)。*官网说明:免费版本每年年底失效,程序会自动退出,需更新库,重新链接。请勿用于正式/生产环境,后果自负。* + +#### 一、用法 +##### 1. client 端 +```java + @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(); + } +``` + +##### 2. server 端 +```java + @Test + public void testServer() throws Exception { + String cert = "keystore/sm2.server1.both.pfx"; + cert = "sm2.auth1/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(); + } +``` + + +#### 二、maven 坐标 + +```xml +cc.kkon +gm-https +0.1.1 +``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3abe30e --- /dev/null +++ b/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + cc.kkon + gm-https + 0.1.1 + jar + + gm-https + http://maven.apache.org + + + 1.8 + 1.8 + 1.8 + UTF-8 + + + + + junit + junit + 4.13.2 + test + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + jakarta.servlet + jakarta.servlet-api + 4.0.4 + + + org.apache.commons + commons-collections4 + 4.4 + + + commons-io + commons-io + 2.11.0 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + + cc.kkon + gmssl_provider + 2022 + + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + true + false + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 8 + 8 + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + + diff --git a/src/main/java/cc/kkon/gmhttps/client/ClientBuilder.java b/src/main/java/cc/kkon/gmhttps/client/ClientBuilder.java new file mode 100644 index 0000000..0a846c9 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/client/ClientBuilder.java @@ -0,0 +1,94 @@ +package cc.kkon.gmhttps.client; + +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +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 javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * 使用HttpClient访问国密https + * + * @author gmssl.cn + */ +public class ClientBuilder { + + + + // 创建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 HttpClient 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 = 30; + 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); + return b.build(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + +} + diff --git a/src/main/java/cc/kkon/gmhttps/client/Response0.java b/src/main/java/cc/kkon/gmhttps/client/Response0.java new file mode 100644 index 0000000..2f1eb8a --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/client/Response0.java @@ -0,0 +1,49 @@ +package cc.kkon.gmhttps.client; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HeaderElement; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/** + * @author yui + */ +public class Response0 { + + + private HttpResponse httpResponse; + + private String content; + + + Response0(HttpResponse httpResponse) throws IOException { + this.httpResponse = httpResponse; + HttpEntity entity = httpResponse.getEntity(); + if (entity != null) { + InputStream in = entity.getContent(); + + this.content = IOUtils.toString(in, StandardCharsets.UTF_8); + in.close(); + } + } + + public String getHeader(String key) { + HeaderElement[] elements = httpResponse.getFirstHeader(key).getElements(); + if (elements != null && elements.length != 0) { + return elements[0].getName(); + } + return null; + } + + public String getContent() { + return this.content; + } + + public HttpResponse getHttpResponse() { + return httpResponse; + } +} diff --git a/src/main/java/cc/kkon/gmhttps/client/SSLRequests.java b/src/main/java/cc/kkon/gmhttps/client/SSLRequests.java new file mode 100644 index 0000000..08363a3 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/client/SSLRequests.java @@ -0,0 +1,120 @@ +package cc.kkon.gmhttps.client; + +import org.apache.commons.collections4.MapUtils; +import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.NameValuePair; +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.client.methods.HttpRequestBase; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicNameValuePair; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.*; + +public class SSLRequests { + + private static final HttpClient client; + + private static final String ENCODING = "UTF-8"; + + + private static final int timeout = 30 * 1000; + + static { + client = ClientBuilder.initGMSSL(); + } + + public static Response0 post(String url, Map params, Map headers) throws IOException { + HttpPost httpPost = new HttpPost(url); + httpPost.setProtocolVersion(HttpVersion.HTTP_1_1); + putHeaders(httpPost, headers); + /* + * 处理参数 + */ + List list = new ArrayList<>(); + if (MapUtils.isNotEmpty(params)) { + Set keySet = params.keySet(); + for (String key : keySet) { + list.add(new BasicNameValuePair(key, params.get(key))); + } + } + + httpPost.setEntity(new UrlEncodedFormEntity(list, ENCODING)); + + HttpResponse response = client.execute(httpPost); + Response0 res0 = new Response0(response); + httpPost.abort(); + return res0; + } + + public static Response0 post4json(String url, String json, Map headers) throws IOException { + HttpPost httpPost = new HttpPost(url); + httpPost.setProtocolVersion(HttpVersion.HTTP_1_1); + putHeaders(httpPost, headers); + + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + + httpPost.setEntity(entity); + + HttpResponse response = client.execute(httpPost); + Response0 res0 = new Response0(response); + httpPost.abort(); + return res0; + } + + public static Response0 get(String url) throws IOException { + return get(url, null, null); + } + + public static Response0 get(String url, Map params, Map headers) throws IOException { + HttpGet httpGet = null; + try { + URIBuilder uriBuilder = new URIBuilder(url); + // 设置请求参数 + if (params != null && params.size() != 0) { + List list = new LinkedList<>(); + for (Map.Entry entry : params.entrySet()) { + list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + } + uriBuilder.setParameters(list); + } + + httpGet = new HttpGet(uriBuilder.build()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + // 设置超时时间 + if (timeout > 0) { + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build(); + httpGet.setConfig(requestConfig); + } + + httpGet.setProtocolVersion(HttpVersion.HTTP_1_1); + + putHeaders(httpGet, headers); + HttpResponse response = client.execute(httpGet); + + Response0 res0 = new Response0(response); + httpGet.abort(); + return res0; + } + + /** + * 设置请求头 + */ + private static void putHeaders(HttpRequestBase httpMethod, Map headers) { + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + httpMethod.setHeader(entry.getKey(), entry.getValue()); + } + } + } +} diff --git a/src/main/java/cc/kkon/gmhttps/client/TrustAllManager.java b/src/main/java/cc/kkon/gmhttps/client/TrustAllManager.java new file mode 100644 index 0000000..439cb05 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/client/TrustAllManager.java @@ -0,0 +1,22 @@ +package cc.kkon.gmhttps.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/main/java/cc/kkon/gmhttps/model/FirstLine.java b/src/main/java/cc/kkon/gmhttps/model/FirstLine.java new file mode 100644 index 0000000..ad1f1c9 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/model/FirstLine.java @@ -0,0 +1,11 @@ +package cc.kkon.gmhttps.model; + +public class FirstLine { + + public String method; + + public String url; + + public String version; + +} diff --git a/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServlet.java b/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServlet.java new file mode 100644 index 0000000..0bc2812 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServlet.java @@ -0,0 +1,25 @@ +package cc.kkon.gmhttps.server; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; + +public abstract class DefaultHttpServlet { + + protected abstract void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException; + + protected abstract void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException; + + final void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = req.getMethod().toUpperCase(); + if (method.equals("GET")) { + this.doGet(req, resp); + } else if (method.equals("POST")) { + this.doPost(req, resp); + } else { + throw new RuntimeException("Not supported method: " + method); + } + } +} diff --git a/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletRequest.java b/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletRequest.java new file mode 100644 index 0000000..04c3bd0 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletRequest.java @@ -0,0 +1,411 @@ +package cc.kkon.gmhttps.server; + +import cc.kkon.gmhttps.model.FirstLine; +import cc.kkon.gmhttps.utils.Strings; +import cc.kkon.gmhttps.utils.Utils; + +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.security.Principal; +import java.util.*; + +public class DefaultHttpServletRequest implements HttpServletRequest { + + private final FirstLine fl; + + private Map headers; + + private Map urlParams; + + private Map bodyParams; + + private byte[] body; + + + public DefaultHttpServletRequest(List headLines, byte[] body) { + String firstLine = headLines.remove(0); + this.fl = Utils.parse1stLine(firstLine); + this.headers = Utils.buildHeaders(headLines); + this.urlParams = new HashMap<>(); + this.bodyParams = new HashMap<>(); + this.body = body; + String url = this.fl.url; + if (url.contains("?")) { + String paramsStr = url.split("\\?")[1]; + this.urlParams = Utils.parseParams(paramsStr); + } + String type = this.getContentType(); + if (body.length != 0 && Strings.isNotBlank(type)) { + if (type.startsWith("application/x-www-form-urlencoded")) { + String con = new String(body, StandardCharsets.UTF_8); + this.bodyParams = Utils.parseParams(con); + } + } + } + + public byte[] getBody() { + return body; + } + + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String s) { + return 0; + } + + @Override + public String getHeader(String s) { + return this.headers.get(s); + } + + @Override + public Enumeration getHeaders(String s) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return null; + } + + @Override + public int getIntHeader(String s) { + return 0; + } + + @Override + public String getMethod() { + return this.fl.method; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String s) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getRequestURI() { + return this.getRequestURL().toString(); + } + + @Override + public StringBuffer getRequestURL() { + String url = this.fl.url; + int i = url.indexOf("?"); + if (i != -1) { + url = url.substring(0, i); + } + return new StringBuffer(url); + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession(boolean b) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException { + return false; + } + + @Override + public void login(String s, String s1) throws ServletException { + + } + + @Override + public void logout() throws ServletException { + + } + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String s) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class aClass) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String s) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public void setCharacterEncoding(String s) throws UnsupportedEncodingException { + + } + + @Override + public int getContentLength() { + return (int) this.getContentLengthLong(); + } + + @Override + public long getContentLengthLong() { + String len = this.headers.getOrDefault("Content-Length", "0"); + return Long.parseLong(len); + } + + @Override + public String getContentType() { + return this.headers.get("Content-Type"); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String s) { + String met = this.getMethod().toUpperCase(); + if (met.equals("GET")) { + return this.urlParams.get(s); + } else if (met.equals("POST")) { + return this.bodyParams.get(s); + } else { + throw new RuntimeException("Not supported method: " + met); + } + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String s) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public void setAttribute(String s, Object o) { + + } + + @Override + public void removeAttribute(String s) { + + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String s) { + return null; + } + + @Override + public String getRealPath(String s) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } +} diff --git a/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletResponse.java b/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletResponse.java new file mode 100644 index 0000000..520386b --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/server/DefaultHttpServletResponse.java @@ -0,0 +1,228 @@ +package cc.kkon.gmhttps.server; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public class DefaultHttpServletResponse implements HttpServletResponse { + + private Map headers; + + private DefaultServletOutputStream out; + + public DefaultHttpServletResponse() { + this.headers = new HashMap<>(); + this.out = new DefaultServletOutputStream(); + } + + @Override + public void addCookie(Cookie cookie) { + + } + + @Override + public boolean containsHeader(String name) { + return false; + } + + @Override + public String encodeURL(String url) { + return null; + } + + @Override + public String encodeRedirectURL(String url) { + return null; + } + + @Override + public String encodeUrl(String url) { + return null; + } + + @Override + public String encodeRedirectUrl(String url) { + return null; + } + + @Override + public void sendError(int sc, String msg) throws IOException { + + } + + @Override + public void sendError(int sc) throws IOException { + + } + + @Override + public void sendRedirect(String location) throws IOException { + + } + + @Override + public void setDateHeader(String name, long date) { + throw new RuntimeException(); + } + + @Override + public void addDateHeader(String name, long date) { + throw new RuntimeException(); + } + + @Override + public void setHeader(String name, String value) { + this.headers.put(name, value); + } + + @Override + public void addHeader(String name, String value) { + throw new RuntimeException(); + } + + @Override + public void setIntHeader(String name, int value) { + throw new RuntimeException(); + } + + @Override + public void addIntHeader(String name, int value) { + throw new RuntimeException(); + } + + @Override + public void setStatus(int sc) { + + } + + @Override + public void setStatus(int sc, String sm) { + + } + + @Override + public int getStatus() { + return 0; + } + + @Override + public String getHeader(String name) { + return this.headers.get(name); + } + + @Override + public Collection getHeaders(String name) { + throw new RuntimeException(); + } + + @Override + public Collection getHeaderNames() { + return this.headers.keySet(); + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return this.out; + } + + @Override + public PrintWriter getWriter() throws IOException { + return null; + } + + @Override + public void setCharacterEncoding(String charset) { + + } + + @Override + public void setContentLength(int len) { + + } + + @Override + public void setContentLengthLong(long len) { + + } + + @Override + public void setContentType(String type) { + + } + + @Override + public void setBufferSize(int size) { + + } + + @Override + public int getBufferSize() { + return 0; + } + + @Override + public void flushBuffer() throws IOException { + + } + + @Override + public void resetBuffer() { + + } + + @Override + public boolean isCommitted() { + return false; + } + + @Override + public void reset() { + + } + + @Override + public void setLocale(Locale loc) { + + } + + @Override + public Locale getLocale() { + return null; + } + + + public byte[] buildResponseMessage() { + byte[] bodyBytes = this.out.toByteArray(); + StringBuilder head = new StringBuilder("HTTP/1.1 200 OK\r\n" + + "Server: GMSSL/1.0\r\n" + + "Content-Length:" + bodyBytes.length + "\r\n" + + "Content-Type: text/plain\r\n"); + for (Map.Entry ent : headers.entrySet()) { + head.append(ent.getKey()).append(": ").append(ent.getValue()).append("\r\n"); + } + head.append("Connection: close\r\n\r\n"); + byte[] headBytes = head.toString().getBytes(); + byte[] bytes = ArrayUtils.addAll(headBytes, bodyBytes); + IOUtils.closeQuietly(this.out); + return bytes; + } +} diff --git a/src/main/java/cc/kkon/gmhttps/server/DefaultServletOutputStream.java b/src/main/java/cc/kkon/gmhttps/server/DefaultServletOutputStream.java new file mode 100644 index 0000000..a651535 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/server/DefaultServletOutputStream.java @@ -0,0 +1,52 @@ +package cc.kkon.gmhttps.server; + +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import org.apache.commons.io.IOUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author yui + */ +public class DefaultServletOutputStream extends ServletOutputStream { + + private ByteArrayOutputStream out; + + + public DefaultServletOutputStream() { + this.out = new ByteArrayOutputStream(); + } + + @Override + public boolean isReady() { + throw new RuntimeException(); + } + + @Override + public void setWriteListener(WriteListener listener) { + throw new RuntimeException(); + } + + @Override + public void write(int b) throws IOException { + this.out.write(b); + } + + @Override + public void close() throws IOException { + IOUtils.close(this.out); + } + + public byte[] toByteArray() { + try { + this.out.flush(); + return this.out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/src/main/java/cc/kkon/gmhttps/server/ReadLine.java b/src/main/java/cc/kkon/gmhttps/server/ReadLine.java new file mode 100644 index 0000000..f3a66ca --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/server/ReadLine.java @@ -0,0 +1,77 @@ +package cc.kkon.gmhttps.server; + +import java.io.*; + +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/main/java/cc/kkon/gmhttps/server/SSLServer.java b/src/main/java/cc/kkon/gmhttps/server/SSLServer.java new file mode 100644 index 0000000..8581e22 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/server/SSLServer.java @@ -0,0 +1,191 @@ +package cc.kkon.gmhttps.server; + + +import cc.kkon.gmhttps.client.TrustAllManager; +import cc.kkon.gmhttps.utils.Strings; +import cc.kkon.gmhttps.utils.Utils; +import org.apache.commons.io.IOUtils; + +import javax.net.ServerSocketFactory; +import javax.net.ssl.*; +import javax.servlet.annotation.WebServlet; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.net.Socket; +import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * 单向认证 + * + * @author gmssl.cn + */ +public class SSLServer { + + private int port; + + private InputStream cert; + + private String certPassword; + + private Map servlets; + + private volatile boolean closed; + + + public SSLServer(int port, InputStream cert, String certPassword) { + this.port = port; + this.cert = cert; + this.certPassword = certPassword; + this.servlets = new HashMap<>(); + } + + public void listen() throws Exception { + Thread thread = new Thread(new Runner0()); + System.out.println("SSLServer started."); + thread.start(); + } + + public void addServlet(String urlPattern, DefaultHttpServlet servlet) { + this.check(urlPattern); + this.servlets.put(urlPattern, servlet); + } + + public void addServlet(DefaultHttpServlet servlet) { + Class clazz = servlet.getClass(); + WebServlet anno = clazz.getAnnotation(WebServlet.class); + String[] value = anno.value(); + for (String val : value) { + this.addServlet(val, servlet); + } + } + + private void check(String urlPattern) { + if (Strings.isBlank(urlPattern)) { + throw new RuntimeException("UrlPattern is blank."); + } + if (this.servlets.containsKey(urlPattern)) { + throw new RuntimeException("UrlPattern existed."); + } + } + + private static SSLServerSocketFactory createServerSocketFactory(KeyStore keyStore, char[] pwd) throws Exception { + TrustManager[] trust = {new TrustAllManager()}; + + KeyManager[] kms = null; + if (keyStore != null) { + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(keyStore, 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); + + return ctx.getServerSocketFactory(); + } + + public void close() { + this.closed = true; + System.out.println("SSLServer closed."); + } + + private class Runner0 implements Runnable { + + @Override + public void run() { + try { + this.listen(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void listen() throws Exception { + ServerSocketFactory fact = null; + SSLServerSocket sslServerSocket = null; + + System.out.println("Usage: java -cp GMExample.jar server.Server1 port"); + + System.out.println("Port=" + port); + + 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"); + char[] certPwdBytes = certPassword.toCharArray(); + pfx.load(cert, certPwdBytes); + + fact = createServerSocketFactory(pfx, certPwdBytes); + sslServerSocket = (SSLServerSocket) fact.createServerSocket(port); + + System.out.println("listening..."); + + while (!closed) { + Socket socket = null; + try { + socket = sslServerSocket.accept(); + System.out.println("client comes"); + + DataInputStream in = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + boolean get = false; + LinkedList reqHeadLines = new LinkedList<>(); + 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 "); + } + reqHeadLines.add(line); + } + String contentLength = Utils.buildHeaders(reqHeadLines).get("Content-Length"); + + byte[] buf = new byte[0]; + // 请求体 + if (!get && Strings.isNotEmpty(contentLength)) { + int len = Integer.parseInt(contentLength); + buf = new byte[len]; + int readLen = in.read(buf); + System.out.println(new String(buf, 0, len)); + } + + + DefaultHttpServletRequest req = new DefaultHttpServletRequest(reqHeadLines, buf); + DefaultHttpServletResponse res = new DefaultHttpServletResponse(); + + String reqURI = req.getRequestURI(); + DefaultHttpServlet servlet = servlets.get(reqURI); + if (servlet == null) { + // TODO: 404 servlet + } + servlet.service(req, res); + + byte[] message = res.buildResponseMessage(); + out.write(message); + out.flush(); + System.out.println("\n\n\n"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + IOUtils.close(socket); + } + } + } + } +} + diff --git a/src/main/java/cc/kkon/gmhttps/utils/ServletUtils.java b/src/main/java/cc/kkon/gmhttps/utils/ServletUtils.java new file mode 100644 index 0000000..c8baee7 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/utils/ServletUtils.java @@ -0,0 +1,15 @@ +package cc.kkon.gmhttps.utils; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class ServletUtils { + + public static void write(HttpServletResponse resp, byte[] bytes) throws IOException { + ServletOutputStream out = resp.getOutputStream(); + out.write(bytes); + out.flush(); + out.close(); + } +} diff --git a/src/main/java/cc/kkon/gmhttps/utils/Strings.java b/src/main/java/cc/kkon/gmhttps/utils/Strings.java new file mode 100644 index 0000000..0c90116 --- /dev/null +++ b/src/main/java/cc/kkon/gmhttps/utils/Strings.java @@ -0,0 +1,259 @@ +package cc.kkon.gmhttps.utils; + + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author yui + */ +public final class Strings extends StringUtils { + public static final String COMMA = ","; + + public static void ifEmpty(String source, Runnable r) { + if (isEmpty(source)) { + r.run(); + } + } + + public static void ifNotEmpty(String source, Consumer consumer) { + if (isNotEmpty(source)) { + consumer.accept(source); + } + } + + public static void ifContains(String source, String sub, Consumer consumer) { + if (isNotEmpty(source)) { + int idx = source.indexOf(sub); + if (idx != -1) { + consumer.accept(idx); + } + } + } + + + public static String htmlSpecialCharsEncode(String str) { + str = str.replaceAll("&", "&"); + str = str.replaceAll("<", "<"); + str = str.replaceAll(">", ">"); + return str; + } + + /** + *

单词首字母转大写

+ * + *
+     * StringUtil.firstLetterToUppercase(null)      = null
+     * StringUtil.firstLetterToUppercase("")        = ""
+     * StringUtil.firstLetterToUppercase(" ")       = " "
+     * StringUtil.firstLetterToUppercase("bob")     = "Bob"
+     * StringUtil.firstLetterToUppercase("1 bob  ") = "1  bob  "
+     * 
+ */ + public static String firstLetterToUpper(String str) { + return (str != null && str.length() >= 1) ? Character.toUpperCase(str.charAt(0)) + str.substring(1) : str; + + } + + private static final Pattern PATTERN = Pattern.compile("\\+"); + + public static String encodeUri(String source) { + if (isEmpty(source)) { + return ""; + } + try { + String res = URLEncoder.encode(source, StandardCharsets.UTF_8.displayName()); + return PATTERN.matcher(res).replaceAll("%20"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static String decodeUri(String source) { + try { + return isEmpty(source) ? "" : URLDecoder.decode(source, StandardCharsets.UTF_8.displayName()); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static String toStr(Object o) { + return o == null ? "" : o.toString(); + } + + public static Integer toInteger(Object o) { + try { + if (o == null || "".equals(o.toString().trim())) { + return null; + } + return Integer.parseInt(o.toString().trim()); + } catch (NumberFormatException e) { + return null; + } + } + + public static List trans(Object list) { + return (List) list; + } + + public static String[] split(String str, String sep) { + if (isEmpty(str)) { + return new String[0]; + } + return Arrays.stream(str.split(sep)) + .filter(StringUtils::isNotEmpty) + .toArray(String[]::new); + + } + + public static String[] split(String str) { + return split(str, COMMA); + } + + public static List split2list(String str, String sep) { + List list = new LinkedList<>(); + if (isEmpty(str)) { + return list; + } + String[] split = split(str, sep); + list.addAll(Arrays.asList(split)); + return list; + } + + public static List 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$$$YIM6&#hjQ?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