🆕 #1090 增加微信支付分和免押租借相关接口

This commit is contained in:
spvycf
2020-05-19 16:25:18 +08:00
committed by GitHub
parent 0bc2cf9ade
commit 8709a9c5a7
26 changed files with 1762 additions and 12 deletions

View File

@@ -0,0 +1,11 @@
package com.github.binarywang.wxpay.v3;
import java.io.IOException;
import org.apache.http.client.methods.HttpUriRequest;
public interface Credentials {
String getSchema();
String getToken(HttpUriRequest request) throws IOException;
}

View File

@@ -0,0 +1,88 @@
package com.github.binarywang.wxpay.v3;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.methods.HttpRequestWrapper;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.execchain.ClientExecChain;
import org.apache.http.util.EntityUtils;
public class SignatureExec implements ClientExecChain {
final ClientExecChain mainExec;
final Credentials credentials;
final Validator validator;
SignatureExec(Credentials credentials, Validator validator, ClientExecChain mainExec) {
this.credentials = credentials;
this.validator = validator;
this.mainExec = mainExec;
}
protected HttpEntity newRepeatableEntity(HttpEntity entity) throws IOException {
byte[] content = EntityUtils.toByteArray(entity);
ByteArrayEntity newEntity = new ByteArrayEntity(content);
newEntity.setContentEncoding(entity.getContentEncoding());
newEntity.setContentType(entity.getContentType());
return newEntity;
}
protected void convertToRepeatableResponseEntity(CloseableHttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
if (entity != null && !entity.isRepeatable()) {
response.setEntity(newRepeatableEntity(entity));
}
}
protected void convertToRepeatableRequestEntity(HttpUriRequest request) throws IOException {
if (request instanceof HttpEntityEnclosingRequestBase) {
HttpEntity entity = ((HttpEntityEnclosingRequestBase) request).getEntity();
if (entity != null && !entity.isRepeatable()) {
((HttpEntityEnclosingRequestBase) request).setEntity(newRepeatableEntity(entity));
}
}
}
@Override
public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request,
HttpClientContext context, HttpExecutionAware execAware) throws IOException, HttpException {
if (request.getURI().getHost().endsWith(".mch.weixin.qq.com")) {
return executeWithSignature(route, request, context, execAware);
} else {
return mainExec.execute(route, request, context, execAware);
}
}
private CloseableHttpResponse executeWithSignature(HttpRoute route, HttpRequestWrapper request,
HttpClientContext context, HttpExecutionAware execAware) throws IOException, HttpException {
HttpUriRequest newRequest = RequestBuilder.copy(request.getOriginal()).build();
convertToRepeatableRequestEntity(newRequest);
// 添加认证信息
newRequest.addHeader("Authorization",
credentials.getSchema() + " " + credentials.getToken(newRequest));
// 执行
CloseableHttpResponse response = mainExec.execute(
route, HttpRequestWrapper.wrap(newRequest), context, execAware);
// 对成功应答验签
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() >= 200 && statusLine.getStatusCode() < 300) {
convertToRepeatableResponseEntity(response);
if (!validator.validate(response)) {
throw new HttpException("应答的微信支付签名验证失败");
}
}
return response;
}
}

View File

@@ -0,0 +1,8 @@
package com.github.binarywang.wxpay.v3;
import java.io.IOException;
import org.apache.http.client.methods.CloseableHttpResponse;
public interface Validator {
boolean validate(CloseableHttpResponse response) throws IOException;
}

View File

@@ -0,0 +1,75 @@
package com.github.binarywang.wxpay.v3;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;
import com.github.binarywang.wxpay.v3.auth.CertificatesVerifier;
import com.github.binarywang.wxpay.v3.auth.PrivateKeySigner;
import com.github.binarywang.wxpay.v3.auth.WechatPay2Credentials;
import com.github.binarywang.wxpay.v3.auth.WechatPay2Validator;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.execchain.ClientExecChain;
public class WechatPayHttpClientBuilder extends HttpClientBuilder {
private Credentials credentials;
private Validator validator;
static final String os = System.getProperty("os.name") + "/" + System.getProperty("os.version");
static final String version = System.getProperty("java.version");
private WechatPayHttpClientBuilder() {
super();
String userAgent = String.format(
"WechatPay-Apache-HttpClient/%s (%s) Java/%s",
getClass().getPackage().getImplementationVersion(),
os,
version == null ? "Unknown" : version);
setUserAgent(userAgent);
}
public static WechatPayHttpClientBuilder create() {
return new WechatPayHttpClientBuilder();
}
public WechatPayHttpClientBuilder withMerchant(String merchantId, String serialNo, PrivateKey privateKey) {
this.credentials =
new WechatPay2Credentials(merchantId, new PrivateKeySigner(serialNo, privateKey));
return this;
}
public WechatPayHttpClientBuilder withCredentials(Credentials credentials) {
this.credentials = credentials;
return this;
}
public WechatPayHttpClientBuilder withWechatpay(List<X509Certificate> certificates) {
this.validator = new WechatPay2Validator(new CertificatesVerifier(certificates));
return this;
}
public WechatPayHttpClientBuilder withValidator(Validator validator) {
this.validator = validator;
return this;
}
@Override
public CloseableHttpClient build() {
if (credentials == null) {
throw new IllegalArgumentException("缺少身份认证信息");
}
if (validator == null) {
throw new IllegalArgumentException("缺少签名验证信息");
}
return super.build();
}
@Override
protected ClientExecChain decorateProtocolExec(final ClientExecChain requestExecutor) {
return new SignatureExec(this.credentials, this.validator, requestExecutor);
}
}

View File

@@ -0,0 +1,161 @@
package com.github.binarywang.wxpay.v3.auth;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.binarywang.wxpay.v3.Credentials;
import com.github.binarywang.wxpay.v3.WechatPayHttpClientBuilder;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.github.binarywang.wxpay.v3.util.PemUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 在原有CertificatesVerifier基础上增加自动更新证书功能
*/
public class AutoUpdateCertificatesVerifier implements Verifier {
private static final Logger log = LoggerFactory.getLogger(AutoUpdateCertificatesVerifier.class);
//证书下载地址
private static final String CertDownloadPath = "https://api.mch.weixin.qq.com/v3/certificates";
//上次更新时间
private volatile Instant instant;
//证书更新间隔时间,单位为分钟
private int minutesInterval;
private CertificatesVerifier verifier;
private Credentials credentials;
private byte[] apiV3Key;
private ReentrantLock lock = new ReentrantLock();
//时间间隔枚举,支持一小时、六小时以及十二小时
public enum TimeInterval {
OneHour(60), SixHours(60 * 6), TwelveHours(60 * 12);
private int minutes;
TimeInterval(int minutes) {
this.minutes = minutes;
}
public int getMinutes() {
return minutes;
}
}
public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key) {
this(credentials, apiV3Key, TimeInterval.OneHour.getMinutes());
}
public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key, int minutesInterval) {
this.credentials = credentials;
this.apiV3Key = apiV3Key;
this.minutesInterval = minutesInterval;
//构造时更新证书
try {
autoUpdateCert();
instant = Instant.now();
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean verify(String serialNumber, byte[] message, String signature) {
if (instant == null || Duration.between(instant, Instant.now()).toMinutes() >= minutesInterval) {
if (lock.tryLock()) {
try {
autoUpdateCert();
//更新时间
instant = Instant.now();
} catch (GeneralSecurityException | IOException e) {
log.warn("Auto update cert failed, exception = " + e);
} finally {
lock.unlock();
}
}
}
return verifier.verify(serialNumber, message, signature);
}
private void autoUpdateCert() throws IOException, GeneralSecurityException {
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withCredentials(credentials)
.withValidator(verifier == null ? (response) -> true : new WechatPay2Validator(verifier))
.build();
HttpGet httpGet = new HttpGet(CertDownloadPath);
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
String body = EntityUtils.toString(response.getEntity());
if (statusCode == 200) {
List<X509Certificate> newCertList = deserializeToCerts(apiV3Key, body);
if (newCertList.isEmpty()) {
log.warn("Cert list is empty");
return;
}
this.verifier = new CertificatesVerifier(newCertList);
} else {
log.warn("Auto update cert failed, statusCode = " + statusCode + ",body = " + body);
}
}
/**
* 反序列化证书并解密
*/
private List<X509Certificate> deserializeToCerts(byte[] apiV3Key, String body)
throws GeneralSecurityException, IOException {
AesUtils decryptor = new AesUtils(apiV3Key);
ObjectMapper mapper = new ObjectMapper();
JsonNode dataNode = mapper.readTree(body).get("data");
List<X509Certificate> newCertList = new ArrayList<>();
if (dataNode != null) {
for (int i = 0, count = dataNode.size(); i < count; i++) {
JsonNode encryptCertificateNode = dataNode.get(i).get("encrypt_certificate");
//解密
String cert = decryptor.decryptToString(
encryptCertificateNode.get("associated_data").toString().replaceAll("\"", "")
.getBytes("utf-8"),
encryptCertificateNode.get("nonce").toString().replaceAll("\"", "")
.getBytes("utf-8"),
encryptCertificateNode.get("ciphertext").toString().replaceAll("\"", ""));
X509Certificate x509Cert = PemUtils
.loadCertificate(new ByteArrayInputStream(cert.getBytes("utf-8")));
try {
x509Cert.checkValidity();
} catch (CertificateExpiredException | CertificateNotYetValidException e) {
continue;
}
newCertList.add(x509Cert);
}
}
return newCertList;
}
}

View File

@@ -0,0 +1,43 @@
package com.github.binarywang.wxpay.v3.auth;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
public class CertificatesVerifier implements Verifier {
private final HashMap<BigInteger, X509Certificate> certificates = new HashMap<>();
public CertificatesVerifier(List<X509Certificate> list) {
for (X509Certificate item : list) {
certificates.put(item.getSerialNumber(), item);
}
}
private boolean verify(X509Certificate certificate, byte[] message, String signature) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initVerify(certificate);
sign.update(message);
return sign.verify(Base64.getDecoder().decode(signature));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
throw new RuntimeException("签名验证过程发生了错误", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("无效的证书", e);
}
}
@Override
public boolean verify(String serialNumber, byte[] message, String signature) {
BigInteger val = new BigInteger(serialNumber, 16);
return certificates.containsKey(val) && verify(certificates.get(val), message, signature);
}
}

View File

@@ -0,0 +1,37 @@
package com.github.binarywang.wxpay.v3.auth;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;
public class PrivateKeySigner implements Signer {
private String certificateSerialNumber;
private PrivateKey privateKey;
public PrivateKeySigner(String serialNumber, PrivateKey privateKey) {
this.certificateSerialNumber = serialNumber;
this.privateKey = privateKey;
}
@Override
public SignatureResult sign(byte[] message) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(message);
return new SignatureResult(
Base64.getEncoder().encodeToString(sign.sign()), certificateSerialNumber);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
throw new RuntimeException("签名计算失败", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("无效的私钥", e);
}
}
}

View File

@@ -0,0 +1,15 @@
package com.github.binarywang.wxpay.v3.auth;
public interface Signer {
SignatureResult sign(byte[] message);
class SignatureResult {
String sign;
String certificateSerialNumber;
public SignatureResult(String sign, String serialNumber) {
this.sign = sign;
this.certificateSerialNumber = serialNumber;
}
}
}

View File

@@ -0,0 +1,5 @@
package com.github.binarywang.wxpay.v3.auth;
public interface Verifier {
boolean verify(String serialNumber, byte[] message, String signature);
}

View File

@@ -0,0 +1,91 @@
package com.github.binarywang.wxpay.v3.auth;
import java.io.IOException;
import java.net.URI;
import java.security.SecureRandom;
import com.github.binarywang.wxpay.v3.Credentials;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WechatPay2Credentials implements Credentials {
private static final Logger log = LoggerFactory.getLogger(WechatPay2Credentials.class);
private static final String SYMBOLS =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final SecureRandom RANDOM = new SecureRandom();
protected String merchantId;
protected Signer signer;
public WechatPay2Credentials(String merchantId, Signer signer) {
this.merchantId = merchantId;
this.signer = signer;
}
public String getMerchantId() {
return merchantId;
}
protected long generateTimestamp() {
return System.currentTimeMillis() / 1000;
}
protected String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
@Override
public final String getSchema() {
return "WECHATPAY2-SHA256-RSA2048";
}
@Override
public final String getToken(HttpUriRequest request) throws IOException {
String nonceStr = generateNonceStr();
long timestamp = generateTimestamp();
String message = buildMessage(nonceStr, timestamp, request);
log.debug("authorization message=[{}]", message);
Signer.SignatureResult signature = signer.sign(message.getBytes("utf-8"));
String token = "mchid=\"" + getMerchantId() + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + signature.certificateSerialNumber + "\","
+ "signature=\"" + signature.sign + "\"";
log.debug("authorization token=[{}]", token);
return token;
}
protected final String buildMessage(String nonce, long timestamp, HttpUriRequest request)
throws IOException {
URI uri = request.getURI();
String canonicalUrl = uri.getRawPath();
if (uri.getQuery() != null) {
canonicalUrl += "?" + uri.getRawQuery();
}
String body = "";
// PATCH,POST,PUT
if (request instanceof HttpEntityEnclosingRequestBase) {
body = EntityUtils.toString(((HttpEntityEnclosingRequestBase) request).getEntity());
}
return request.getRequestLine().getMethod() + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonce + "\n"
+ body + "\n";
}
}

View File

@@ -0,0 +1,55 @@
package com.github.binarywang.wxpay.v3.auth;
import java.io.IOException;
import com.github.binarywang.wxpay.v3.Validator;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WechatPay2Validator implements Validator {
private static final Logger log = LoggerFactory.getLogger(WechatPay2Validator.class);
private Verifier verifier;
public WechatPay2Validator(Verifier verifier) {
this.verifier = verifier;
}
@Override
public final boolean validate(CloseableHttpResponse response) throws IOException {
Header serialNo = response.getFirstHeader("Wechatpay-Serial");
Header sign = response.getFirstHeader("Wechatpay-Signature");
Header timestamp = response.getFirstHeader("Wechatpay-TimeStamp");
Header nonce = response.getFirstHeader("Wechatpay-Nonce");
// todo: check timestamp
if (timestamp == null || nonce == null || serialNo == null || sign == null) {
return false;
}
String message = buildMessage(response);
return verifier.verify(serialNo.getValue(), message.getBytes("utf-8"), sign.getValue());
}
protected final String buildMessage(CloseableHttpResponse response) throws IOException {
String timestamp = response.getFirstHeader("Wechatpay-TimeStamp").getValue();
String nonce = response.getFirstHeader("Wechatpay-Nonce").getValue();
String body = getResponseBody(response);
return timestamp + "\n"
+ nonce + "\n"
+ body + "\n";
}
protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
}
}

View File

@@ -0,0 +1,107 @@
package com.github.binarywang.wxpay.v3.util;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesUtils {
static final int KEY_LENGTH_BYTE = 32;
static final int TAG_LENGTH_BIT = 128;
private final byte[] aesKey;
public AesUtils(byte[] key) {
if (key.length != KEY_LENGTH_BYTE) {
throw new IllegalArgumentException("无效的ApiV3Key长度必须为32个字节");
}
this.aesKey = key;
}
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
throws GeneralSecurityException, IOException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData);
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
public static String decryptToString(String associatedData, String nonce, String ciphertext,String apiV3Key)
throws GeneralSecurityException, IOException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes());
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData.getBytes());
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
public static String createSign(Map<String, String> map, String mchKey) {
Map<String, String> params = map;
SortedMap<String, String> sortedMap = new TreeMap<>(params);
StringBuilder toSign = new StringBuilder();
for (String key : sortedMap.keySet()) {
String value = params.get(key);
if ("sign".equals(key) || StringUtils.isEmpty(value)) {
continue;
}
toSign.append(key).append("=").append(value).append("&");
}
toSign.append("key=" + mchKey);
return HMACSHA256(toSign.toString(), mchKey);
}
public static String HMACSHA256(String data, String key) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@@ -0,0 +1,60 @@
package com.github.binarywang.wxpay.v3.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class PemUtils {
public static PrivateKey loadPrivateKey(InputStream inputStream) {
try {
ByteArrayOutputStream array = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
array.write(buffer, 0, length);
}
String privateKey = array.toString("utf-8")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
} catch (IOException e) {
throw new RuntimeException("无效的密钥");
}
}
public static X509Certificate loadCertificate(InputStream inputStream) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书", e);
}
}
}