diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml
index 48ca0837..6059f214 100644
--- a/weixin4j-base/pom.xml
+++ b/weixin4j-base/pom.xml
@@ -71,5 +71,10 @@
3.0.2
true
+
+ org.bouncycastle
+ bcprov-jdk16
+ 1.46
+
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
index 462f8faa..c058041d 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
@@ -1,12 +1,22 @@
package com.foxinmy.weixin4j.http.weixin;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.security.KeyStore;
+import java.security.*;
+import java.security.cert.CertificateFactory;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.bind.DatatypeConverter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.HttpClient;
@@ -29,6 +39,8 @@ import com.foxinmy.weixin4j.logging.InternalLogLevel;
import com.foxinmy.weixin4j.logging.InternalLogger;
import com.foxinmy.weixin4j.logging.InternalLoggerFactory;
import com.foxinmy.weixin4j.util.Consts;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
/**
* 负责微信请求的执行
@@ -56,6 +68,18 @@ public class WeixinRequestExecutor {
this.httpClient = HttpClientFactory.getInstance(params);
}
+ private static final Pattern CERT_PATTERN = Pattern.compile(
+ "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*CERTIFICATE[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
+ private static final Pattern KEY_PATTERN = Pattern.compile(
+ "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
/**
* Post方法执行微信请求
*
@@ -250,4 +274,69 @@ public class WeixinRequestExecutor {
params.setSSLContext(sslContext);
return new WeixinRequestExecutor(params);
}
+
+ /**
+ * 使用PEM格式证书创建SSL微信请求对象
+ *
+ * @param pemCertificate
+ * PEM格式证书内容
+ * @param pemPrivateKey
+ * PEM格式证书私钥
+ * @return
+ */
+ public WeixinRequestExecutor createSSLRequestExecutor(String password, String pemCertificate, String pemPrivateKey) throws WeixinException{
+ Security.addProvider(new BouncyCastleProvider());
+
+ try {
+ byte[] certBytes = parseDERFromPEM(pemCertificate);
+ byte[] keyBytes = parseDERFromPEM(pemPrivateKey);
+
+ char[] passwordChars = password.toCharArray();
+ X509Certificate cert = generateCertificateFromDER(certBytes);
+ RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
+
+ KeyStore keystore = KeyStore.getInstance("JKS");
+ keystore.load(null);
+ keystore.setCertificateEntry("cert-alias", cert);
+ keystore.setKeyEntry("key-alias", key, passwordChars, new X509Certificate[] {cert});
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(keystore, passwordChars);
+
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(kmf.getKeyManagers(), null, new java.security.SecureRandom());
+
+ return createSSLRequestExecutor(context);
+ } catch (Exception e) {
+ throw new WeixinException("Certificate load error", e);
+ }
+
+ }
+
+ private static byte[] parseDERFromPEM(String data) throws KeyStoreException {
+ Matcher matcher = CERT_PATTERN.matcher(data);
+ String content = "";
+ if(!matcher.find()){
+ matcher = KEY_PATTERN.matcher(data);
+ if(!matcher.find()){
+ throw new KeyStoreException("found no private key or certificate from content:"+ data);
+ }
+ }
+ content = matcher.group(1);
+ return DatatypeConverter.parseBase64Binary(content);
+ }
+
+ private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
+
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+
+ return (RSAPrivateKey)factory.generatePrivate(spec);
+ }
+
+ protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+
+ return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
+ }
}