package com.example.demo.utils.http;
import javax.crypto.Cipher;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Date;
public class CertificateUtils {
/**
* 实例化证书工厂(双向认证)
* @param keyStorePwd 密钥库及信任库密码(一样的)
* @param keyStorePath 密钥库路径
* @param trustKeyStorePath 信任库路径
* @param sunX509 算法名称 (该方式<KeyManagerFactory>默认填写SunX509,通过CertificateFactory获取证书写X.509)
* @param keyStoreType 密钥库类型 pkcs12或者JKS
* @param ssl 请求协议的标准名称.SSL或STL
* @return
*/
public static SSLSocketFactory getSf(String keyStorePwd, String keyStorePath, String trustKeyStorePath,
String sunX509,String keyStoreType,String ssl) throws Exception {
// 初始化密钥库 sunX509 KeyManagerFactory.getDefaultAlgorithm()
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(sunX509);
KeyStore keyStore = getKeyStore(keyStorePath, keyStorePwd,keyStoreType);
keyManagerFactory.init(keyStore, keyStorePwd.toCharArray());
// 初始化信任库
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(sunX509);
KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, keyStorePwd,keyStoreType);
trustManagerFactory.init(trustkeyStore);
// 初始化SSL上下文
SSLContext ctx = SSLContext.getInstance(ssl);
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
SSLSocketFactory sf = ctx.getSocketFactory();
return sf;
}
/**
* 实例化证书工厂(单向认证)
* @return
*/
public static SSLSocketFactory getSf() throws Exception {
InputStream ins = new FileInputStream("aaa.cer");
CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");
java.security.cert.Certificate cer = cerFactory.generateCertificate(ins);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); //问2
keyStore.load(null, null);
keyStore.setCertificateEntry("trust", cer);
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init( null, trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory sf = sslContext.getSocketFactory();
return sf;
}
/**
* 获取密钥库
*/
private static KeyStore getKeyStore(String keyStorePath, String password,String keyStoreType) throws Exception {
FileInputStream fis = new FileInputStream(keyStorePath);
KeyStore ks = KeyStore.getInstance(keyStoreType);
ks.load(fis, password.toCharArray());
fis.close();
return ks;
}
/**
* 获得证书(通过密钥库获得证书)
* @param keystorePath 密钥库路径
* @param keystorePwd 密钥库密码
* @param keystoreType 密钥库类型
* @param keyAlias 密钥库别名
* @return
*/
private static X509Certificate getCert(String keystorePath, String keystorePwd, String keystoreType, String keyAlias)
throws Exception {
KeyStore keyStore = getKeyStore(keystorePath, keystorePwd, keystoreType);
X509Certificate x509Certificate = (X509Certificate) keyStore.getCertificate(keyAlias);
return x509Certificate;
}
/**
* 获得证书Certificate(通过证书工厂取得证书) 暂未使用
*
* @param certificatePath
* @return
* @throws Exception
*/
private static X509Certificate getCertificate(String certificatePath)
throws Exception {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream(certificatePath);
X509Certificate certificate =(X509Certificate) certificateFactory.generateCertificate(in);
in.close();
return certificate;
}
/**
* 获得证书的base64字符串
* @param keystorePath 证书文件路径
* @param keystorePwd 证书文件密码
* @param keystoreType 证书文件类型
* @param keyAlias 证书文件别名
* @return
*/
public static String getBase64Cert(String keystorePath, String keystorePwd, String keystoreType, String keyAlias){
X509Certificate x509Certificate = null;
try {
x509Certificate = getCert(keystorePath,keystorePwd,keystoreType,keyAlias);
byte[] bs = x509Certificate.getEncoded();
String merchantCert = Base64.getEncoder().encodeToString(bs);
return merchantCert;
} catch (Exception e) {
return null;
}
}
/**
* 使用私钥对数据进行加密(请求发送数据的时候使用)
* @param info 需要校验的请求参数的json字符串或xml
* @param keyStorePath 密钥库路径
* @param keystorePwd 密钥库密码
* @param keyStoreType 密钥库类型
* @param prtAlias 密钥库别名
* @param mrSigAlgName 签名方式 SHA256WithRSA 或 MD5 或 RSA
* @return
*/
public static String encodeBySK(String info,String keyStorePath, String keystorePwd,String keyStoreType,String prtAlias,String mrSigAlgName) {
try {
/**
* getKeyStore(keyStorePath, keystorePwd)
* PrivateKey key = (PrivateKey) ks.getKey(alias,aliasPwd.toCharArray());
* 一般情况下证书库密码与别名密码不一样
*/
KeyStore keyStore = getKeyStore(keyStorePath,keystorePwd,keyStoreType);
PrivateKey pk = (PrivateKey) keyStore.getKey(prtAlias, keystorePwd.toCharArray());
Signature sign = Signature.getInstance(mrSigAlgName);
sign.initSign(pk);
sign.update(info.getBytes());
byte[] signed = sign.sign();
String signValue = Base64.getEncoder().encodeToString(signed);
return signValue;
}catch (Exception e){
return null;
}
}
/**
* 使用私钥对数据进行解密(用于接受参数解密) 暂时未启用
* @param data
* @param keyStorePath
* @param keyStorePwd
* @param prtAlias
* @param keyStoreType
* @return
*/
@Deprecated
public static byte[] decodeBySK(String data, String keyStorePath, String keyStorePwd, String prtAlias, String keyStoreType) {
try {
// 取得私钥
KeyStore keyStore = getKeyStore(keyStorePath,keyStorePwd,keyStoreType);
PrivateKey pk = (PrivateKey) keyStore.getKey(prtAlias, keyStorePwd.toCharArray());
// 对数据加密
Cipher cipher = Cipher.getInstance(pk.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pk);
return cipher.doFinal(data.getBytes());
}catch (Exception e){
return null;
}
}
/**
* 公钥加密 暂时未启用
* @param data 签名的json参数或xml
* @param certificatePath 证书文件路径
* @return
* @throws Exception
*/
@Deprecated
public static String encryptByPK(String data, String certificatePath) {
try {
// 取得公钥
X509Certificate certificate = getCertificate(certificatePath);
PublicKey publicKey = certificate.getPublicKey();
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return new String(cipher.doFinal(data.getBytes()));
}catch (Exception e){
return null;
}
}
/**
* 公钥解密 (暂时未启用)
* @param data 签名数据
* @param certificatePath 证书文件路径
* @return
* @throws Exception
*/
@Deprecated
public static String decryptByPK(String data, String certificatePath) {
try{
// 取得公钥
X509Certificate certificate = getCertificate(certificatePath);
PublicKey publicKey = certificate.getPublicKey();
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(cipher.doFinal(data.getBytes()));
}catch (Exception e){
return null;
}
}
/**
* 验证证书是否过期 暂时未启用
* @param certificatePath
* @return
*/
@Deprecated
public static boolean verifyCertificate(String certificatePath){
boolean status = true;
try{
X509Certificate x509Certificate=getCertificate(certificatePath);
x509Certificate.checkValidity(new Date());
}catch (Exception e){
status = false;
}
return status;
}
/**
* 验证证书是否过期 暂时未启用
* @return
*/
@Deprecated
public static boolean verifyCertificate(String keyStorePath, String alias, String password,String keyStoreType){
boolean status = true;
try{
X509Certificate x509Certificate=getCert(keyStorePath,password,keyStoreType,alias);
x509Certificate.checkValidity(new Date());
}catch (Exception e){
status = false;
}
return status;
}
}
package com.example.demo.utils.http;
import javax.net.ssl.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import static com.example.demo.configs.CertConfigUtils.*;
public class URLConnect {
/**
* 获取http请求链接
* @return connection
*/
public static HttpURLConnection httpCollect(String uri,String methods) throws Exception {
methods = methods.toUpperCase(); // 将小写全部转换为大写
URL url = new URL(uri); // 创建链接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();// 通过远程url连接对象打开一个连接,强转成httpURLConnection类
connection.setRequestMethod(methods); // 设置连接方式:get/post
return connection;
}
/**
* 获取不需要验证的https请求
* @return
*/
public static HttpsURLConnection notVerifyHttpsCollect(String uri,String methods) throws Exception{
/**
* 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
*/
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {
// TODO: 2018/9/10
/**
* 该方法检查客户端的证书,若不信任该证书则抛出异常。
* 由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。
* JSSE中,默认的信任管理器类为TrustManager
*/
}
@Override
public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {
// TODO: 2018/9/10
/**
* 该方法检查服务器的证书,若不信任该证书同样抛出异常。
* 通过自己实现该方法,可以使之信任我们指定的任何证书。
* 在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。
*/
}
@Override
public X509Certificate[] getAcceptedIssuers() {
// TODO: 2018/9/10
// 返回受信任的X509证书数组。
return null;
}
};
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { trustManager };
SSLContext ctx = SSLContext.getInstance("SSL","SunJSSE"); // SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tm, new SecureRandom()); // ctx.init(new KeyManager[0],tm, new SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = ctx.getSocketFactory();
// 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象
URL url = new URL(uri);
HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection();
httpsConn.setSSLSocketFactory(ssf);
httpsConn.setHostnameVerifier((arg0,arg1) -> true);
httpsConn.setRequestMethod(methods);
return httpsConn;
}
public static HttpsURLConnection httpsCollect(String uri,String methods){
try{
// eg: 123456,D:/crt/abc.pfx,D:/crt/abd.pfx,JKS,SunX509,pkcs12,SSL
SSLSocketFactory ssf = null;
if(isCert == 2){
ssf = CertificateUtils.getSf(mrKeyPwd,mrKeyAlias,trustPath,sunX509,mrKeystoreType,SSL);
}else if(isCert == 3){
CertificateUtils.getSf();
}else {
return null;
}
URL url = new URL(uri);
HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection();
httpsConn.setSSLSocketFactory(ssf);
httpsConn.setHostnameVerifier((arg0,arg1) -> true);
httpsConn.setRequestMethod(methods);
return httpsConn;
}catch (Exception e){
return null;
}
}
/**
* 设置请求头
* @throws Exception
*/
public static URLConnection doService(String uri,String method) throws Exception {
URLConnection conn = null;
if(uri.indexOf("https://") != -1){
if(isCert ==1){
conn=notVerifyHttpsCollect(uri,method);
}else {
conn = httpsCollect(uri,method);
}
}else {
conn = httpCollect(uri,method);
}
conn.setConnectTimeout(8000); // 设置连接主机服务器的超时时间:8秒
conn.setReadTimeout(8000); // 设置读取远程返回的数据时间:8秒
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 处理post请求参数
if(method.equals("POST")){
conn.setDoInput(true);
conn.setDoOutput(true); // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
conn.setUseCaches(false); // post请求缓存设为false
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");//参数请求格式 或者 application/x-www-form-urlencoded;charset=utf-8
/**
* String contentType = conn.getHeaderField("Content-Type"); 通过getHeaderField()方法可以读取响应头
* conn.setRequestProperty("Authorization", "token 111"); 设置鉴权信息:Authorization: token 111(即添加token)
* conn.setRequestProperty("Cookie",StringUtils.join(cookieManager.getCookieStore().getCookies(), ";")); 添加cookie
* String cookiesHeader = conn.getHeaderField("Set-Cookie");List<HttpCookie> cookies = HttpCookie.parse(cookiesHeader); 获取cookie
* Optional<HttpCookie> usernameCookie = cookies.stream().findAny().filter(cookie -> cookie.getName().equals("username"));检查cookie中是否含有username
* connection.getResponseCode() 得到响应码 200 500 等 需要写在子类中
* conn.setInstanceFollowRedirects(false); 处理重定向 需要写在子类中
*/
}
return conn;
}
}
package com.example.demo.utils.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URLConnection;
public class URLClient {
private static Logger log = LoggerFactory.getLogger(URLClient.class);
/**
* 发送http或https类型的get请求
* @param uri
* @return
*/
public static String doGet(String uri) {
return httpRequest(uri,"GET",null);
}
/**
* 发送http或https类型的post请求(忽略验证的https请求)
* @param uri
* @param outputStr
* @return
*/
public static String doPost(String uri, String outputStr) {
return httpRequest(uri,"POST",outputStr);
}
private static String httpRequest(String uri,String method,String outputStr){
try {
URLConnection conn =URLConnect.doService(uri,method);
// 如果请求参数不为空
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuffer buffer = new StringBuffer();
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
// 关闭链接
if(conn instanceof HttpURLConnection){
((HttpURLConnection)conn).disconnect();
}else {
((HttpsURLConnection)conn).disconnect();
}
return buffer.toString();
} catch (Exception e) {
log.error("http或者https请求异常,请留意错误信息");
return null;
}
}
}
package com.example.demo.configs;
import java.io.IOException;
import java.util.Properties;
public class CertConfigUtils {
private static Properties props = new Properties();
public static Integer isCert = 1;
// 客户端密码
public static String mrKeyPwd = null;
// 客户端别名
public static String mrKeyAlias = null;
// 客户端路径
public static String mrKeyPath = null;
// 信任的文件
public static String trustPath = null;
public static String mrSigAlgName = "SHA256WithRSA";
public static String sunX509 = "SunX509";
public static String mrKeystoreType = "pkcs12";
public static String SSL = "SSL";
static {
try {
props.load(CertConfigUtils.class.getClassLoader().getResourceAsStream("cert_config.properties"));
mrKeyPwd = props.getProperty("mrKeyPwd");
mrKeyAlias = props.getProperty("mrKeyAlias");
mrKeyPath =CertConfigUtils.class.getClassLoader().getResource(props.getProperty("mrKeyPath")).getPath().substring(1);
trustPath = props.getProperty("trustPath");
} catch (IOException e) {
isCert = 1;
}
}
}
使用方法
1.将以上4个对象导入到项目中
2.如果是http请求
String result = URLClient.doPost(uri,request.toString());
3.不需要验证的https请求:
String result = URLClient.doPost(uri,request.toString())
4.如果是单项认证的https请求
- 设置CertConfigUtils.isCert=3
- 放置证书文件到目录下(一般为crt格式,cer格式)
- 创建cert_config.properties配置文件并配置证书路径
- 在里面写下trustPath的路径
- String result = URLClient.doPost(uri,request.toString())
5.如果是双向认证的https
- 设置CertConfigUtils.isCert=4
- 放置证书文件到目录下(4个文件。s.crt,s.store,m.crt,m.store)
- 创建cert_config.properties配置文件,配置路径,公钥,私钥
- String result = URLClient.doPost(uri,request.toString())