HTTPS协议加密传输
- 传输方式:采用https传输
- 提交方式:POST
- 数据格式:提交和返回都采用json格式
- 字符编码:统一采用utf-8字符编码
- 签名算法:证书签名
如何生成证书(非对称加密算法)
下载openssl工具
- 生成秘钥:
openssl genrsa -out rsa_private_key.pem 2048`
- 格式转换:
#控制台输出的结果就是pkcs8格式的秘钥
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
- 生成公钥:
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
- 生成证书
#新建私钥
openssl req -newkey rsa:2048 -nodes -keyout rsa_private_key.pem -x509 -days 3650
#根据已有私钥
openssl req -new -key rsa_private_key.pem -x509 -days 3650 -config "C:\Users\33088\Downloads\openssl-0.9.8h-1-bin\share\openssl.cnf" -out C:\Users\33088\Downloads\openssl-0.9.8h-1-bin\bin\public.cer
- 生成pfx文件
openssl pkcs12 -export -out private.pfx -inkey rsa_private_key.pem -in public.cer
- 解析pfx文件
openssl pkcs12 -in private.pfx -nodes -out private.pem
使用的工具类
RSAUtil
package com.sinosoft.newstandardcore.web.utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Enumeration;
/**
* @Author: Eric
**/
public class RSAUtil {
/**
* 加密算法RSA
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* 签名算法
*/
private static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* RSA加密明文分段最大长度
*/
private static final int MAX_ENCRYPT_SPLIT = 117;
/**
* RSA解密密分段最大长度.1024位加密用128,1028位用256
*/
private static final int MAX_DECRYPT_SPLIT = 256;
private static Cipher cipher;
static {
try {
cipher = Cipher.getInstance(KEY_ALGORITHM);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
/**
* 获取公钥
*
* @param publicPemFile 公钥文件路径
*/
public static PublicKey getPublicKeyFromPem(File publicPemFile) throws Exception {
byte[] keyBytes = getKeyBytes(publicPemFile);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(keyBytes);
return keyFactory.generatePublic(encodedKeySpec);
}
/**
* 根据*.cer文件获取公钥
*
* @param cerFile *.cer文件
*/
public static PublicKey getPublicKeyFromCer(File cerFile) {
try {
// 读取证书文件
InputStream inputStream = new FileInputStream(cerFile);
// 创建X509工厂类
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// 创建证书对象
X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
inputStream.close();
return x509Certificate.getPublicKey();
} catch (Exception e) {
System.out.println("解析证书出错!");
e.printStackTrace();
}
return null;
}
/**
* 获取私钥
*
* @param privatePemFile 私钥文件路径
*/
public static PrivateKey getPrivateKeyFromPem(File privatePemFile) throws Exception {
byte[] keyBytes = getKeyBytes(privatePemFile);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
return keyFactory.generatePrivate(encodedKeySpec);
}
/**
* 根据*.pfx文件获取私钥
*
* @param privatePfxFile *.pfx文件
* @param password 文件密码
*/
public static PrivateKey getPrivateKeyFromPfx(File privatePfxFile, String password) {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream fileInputStream = new FileInputStream(privatePfxFile);
keyStore.load(fileInputStream, password.toCharArray());
Enumeration enumas = keyStore.aliases();
String aliases = null;
if (enumas.hasMoreElements()) {
aliases = (String) enumas.nextElement();
}
return (PrivateKey) keyStore.getKey(aliases, password.toCharArray());
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
System.out.println("解析证书出错!");
e.printStackTrace();
}
return null;
}
/**
* 根据*.pem文件获取key
*
* @param pemFile *.pem文件
*/
private static byte[] getKeyBytes(File pemFile) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader(pemFile));
StringBuffer key = new StringBuffer();
String line = bufferedReader.readLine();
while (line != null) {
if (line.charAt(0) != '-') {
key.append(line).append(System.getProperty("line.separator"));
}
line = bufferedReader.readLine();
}
return Base64.decodeBase64(key.toString().getBytes());
}
/**
* RSA加密
*
* @param plainBytes 明文
* @param key key
*/
public static byte[] encrypt(byte[] plainBytes, Key key) throws InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
cipher.init(Cipher.ENCRYPT_MODE, key);
return sectionAnalysis(plainBytes, MAX_ENCRYPT_SPLIT);
}
/**
* RSA解密
*
* @param cipherBytes 密文
* @param key key
*/
public static byte[] decrypt(byte[] cipherBytes, Key key) throws InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
cipher.init(Cipher.DECRYPT_MODE, key);
return sectionAnalysis(cipherBytes, MAX_DECRYPT_SPLIT);
}
/**
* 分段解析
*
* @param bytes 需要解析的内容
* @param splitLength 分段最大长度
*/
private static byte[] sectionAnalysis(byte[] bytes, int splitLength) throws BadPaddingException, IllegalBlockSizeException, IOException {
int totalLength = bytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] temp;
int i = 0;
// 对数据分段解密
while (totalLength - offSet > 0) {
if (totalLength - offSet > splitLength) {
temp = cipher.doFinal(bytes, offSet, splitLength);
} else {
temp = cipher.doFinal(bytes, offSet, totalLength - offSet);
}
out.write(temp, 0, temp.length);
i++;
offSet = i * splitLength;
}
byte[] cipherBytes = out.toByteArray();
out.close();
return cipherBytes;
}
/**
* 加签
*/
public static byte[] sign(byte[] bytes, PrivateKey privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(bytes);
return Base64.encodeBase64(signature.sign());
}
/**
* 验签
*/
public static boolean verify(byte[] bytes, PublicKey publicKey, byte[] sign) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(bytes);
return signature.verify(Base64.decodeBase64(sign));
}
public static void main(String[] args) throws Exception {
String prefix = System.getProperty("user.dir");
PublicKey publicKey = getPublicKeyFromCer(new File(prefix + "\\newstandard-core\\src\\main\\resources\\openssl\\own_public.cer"));
PrivateKey privateKey = getPrivateKeyFromPfx(new File(prefix + "\\newstandard-core\\src\\main\\resources\\openssl\\own_private.pfx"), "Kk123456");
String plainText = "这是明文";
//公钥加密,私钥解密
byte[] enBytes1 = encrypt(plainText.getBytes(), publicKey);
byte[] deBytes1 = decrypt(enBytes1, privateKey);
System.out.println(new String(deBytes1));
//私钥加密,公钥解密
byte[] enBytes2 = encrypt(plainText.getBytes(), privateKey);
byte[] deBytes2 = decrypt(enBytes2, publicKey);
System.out.println(new String(deBytes2));
//加签、验签
byte[] signBytes = sign(enBytes2, privateKey);
boolean verify = verify(enBytes2, publicKey, signBytes);
System.out.println(verify);
}
}
##JacksonUtil
package com.sinosoft.newstandardcore.web.utils;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
/**
* 简单封装Jackson,实现JSON String<->Java Object的Mapper.
* 封装不同的输出风格, 使用不同的builder函数创建实例.
*
* @Author: Eric
*/
public class JacksonUtil extends ObjectMapper {
private static final long serialVersionUID = 1L;
private static Logger logger = LoggerFactory.getLogger(JacksonUtil.class);
/**
* 当前类的实例持有者(静态内部类,延迟加载,懒汉式,线程安全的单例模式)
*/
private static final class JsonMapperHolder {
private static final JacksonUtil INSTANCE = new JacksonUtil();
}
private JacksonUtil() {
// 为Null时不序列化
this.setSerializationInclusion(Include.ALWAYS);
// 允许单引号
this.configure(Feature.ALLOW_SINGLE_QUOTES, true);
// 允许不带引号的字段名称
this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 设置时区
this.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 遇到空值处理为空串
// this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
// @Override
// public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// jgen.writeString("");
// }
// });
//格式化Date类型的数据
this.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
SimpleModule simpleModule = new SimpleModule();
//序列换成json时,将所有的long变成string 因为js中得数字类型不能包含所有的java long值
simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
this.registerModule(simpleModule);
}
/**
* Object可以是POJO,也可以是Collection或数组。
* 如果对象为Null, 返回"null".
* 如果集合为空集合, 返回"[]".
*/
private String toJsonString(Object object) {
try {
return this.writeValueAsString(object);
} catch (IOException e) {
logger.warn("write to json string error:" + object, e);
return null;
}
}
/**
* 输出JSONP格式数据.
*/
private String toJsonpString(String functionName, Object object) {
return toJsonString(new JSONPObject(functionName, object));
}
/**
* 反序列化POJO或简单Collection如List<String>.
* 如果JSON字符串为Null或"null"字符串, 返回Null.
* 如果JSON字符串为"[]", 返回空集合.
* 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)
*/
private <T> T fromJsonString(String jsonString, Class<T> clazz) {
if (StringUtils.isEmpty(jsonString) || "<CLOB>".equals(jsonString)) {
return null;
}
try {
return this.readValue(jsonString, clazz);
} catch (IOException e) {
logger.warn("parse json string error:" + jsonString, e);
return null;
}
}
/**
* 反序列化复杂Collection如List<Bean>, 先使用函数createCollectionType构造类型,然后调用本函数.
*
* @see #createCollectionType(Class, Class...)
*/
@SuppressWarnings("unchecked")
public <T> T fromJsonString(String jsonString, JavaType javaType) {
if (StringUtils.isEmpty(jsonString) || "<CLOB>".equals(jsonString)) {
return null;
}
try {
return (T) this.readValue(jsonString, javaType);
} catch (IOException e) {
logger.warn("parse json string error:" + jsonString, e);
return null;
}
}
/**
* 构造泛型的Collection Type如:
* ArrayList<MyBean>, 则调用constructCollectionType(ArrayList.class,MyBean.class)
* HashMap<String,MyBean>, 则调用(HashMap.class,String.class, MyBean.class)
*/
public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return this.getTypeFactory().constructParametricType(collectionClass, elementClasses);
}
/**
* 当JSON里只含有Bean的部分属性時,更新一个已存在Bean,只覆盖该部分的属性.
*/
@SuppressWarnings("unchecked")
public <T> T update(String jsonString, T object) {
try {
return (T) this.readerForUpdating(object).readValue(jsonString);
} catch (IOException e) {
logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
}
return null;
}
/**
* 设定是否使用Enum的toString函数来读写Enum,
* 为False实时使用Enum的name()函数来读写Enum, 默认为False.
* 注意本函数一定要在Mapper创建后, 所有的读写动作之前调用.
*/
public JacksonUtil enableEnumUseToString() {
this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
return this;
}
/**
* 取出Mapper做进一步的设置或使用其他序列化API.
*/
public ObjectMapper getMapper() {
return this;
}
/**
* 获取当前实例
*/
public static JacksonUtil getInstance() {
return JsonMapperHolder.INSTANCE;
}
/**
* 对象转换为JSON字符串
*/
public static String toJson(Object object) {
return JacksonUtil.getInstance().toJsonString(object);
}
/**
* 对象转换为JSONP字符串
*/
public static String toJsonp(String functionName, Object object) {
return JacksonUtil.getInstance().toJsonpString(functionName, object);
}
/**
* JSON字符串转换为对象
*/
@SuppressWarnings("unchecked")
public static <T> T fromJson(String jsonString, Class<?> clazz) {
return (T) JacksonUtil.getInstance().fromJsonString(jsonString, clazz);
}
/**
* JSON字符串转换为 List<Map<String, Object>>
*/
public static List<Map<String, Object>> fromJsonForMapList(String jsonString) {
List<Map<String, Object>> result = new ArrayList<>();
if (StringUtils.startsWith(jsonString, "{")) {
Map<String, Object> map = fromJson(jsonString, Map.class);
if (map != null) {
result.add(map);
}
} else if (StringUtils.startsWith(jsonString, "[")) {
List<Map<String, Object>> list = fromJson(jsonString, List.class);
if (list != null) {
result = list;
}
}
return result;
}
}
EncryptionDecryptionHandler
package com.sinosoft.newstandardcore.common;
import com.sinosoft.newstandardcore.web.utils.JacksonUtil;
import com.sinosoft.newstandardcore.web.utils.RSAUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* @Author: Eric
**/
public class EncryptionDecryptionHandler {
private Logger logger = LoggerFactory.getLogger(EncryptionDecryptionHandler.class);
private PublicKey cPlusPublicKey;
private PrivateKey ownPrivateKey;
public EncryptionDecryptionHandler(String publicFileName, String privateFileName, String password) {
try {
InputStream stream;
File file;
stream = ClassUtils.class.getClassLoader().getResourceAsStream("openssl/" + publicFileName);
file = new File(publicFileName);
FileUtils.copyInputStreamToFile(stream, file);
if (publicFileName.endsWith(".cer")) {
//根据*.cer文件获取公钥
this.cPlusPublicKey = RSAUtil.getPublicKeyFromCer(file);
} else if (publicFileName.endsWith(".pem")) {
//公钥文件路径
this.cPlusPublicKey = RSAUtil.getPublicKeyFromPem(file);
}
stream = ClassUtils.class.getClassLoader().getResourceAsStream("openssl/" + privateFileName);
file = new File(publicFileName);
FileUtils.copyInputStreamToFile(stream, file);
if (privateFileName.endsWith(".pfx")) {
//.pfx文件获取私钥
this.ownPrivateKey = RSAUtil.getPrivateKeyFromPfx(file, password);
} else {
//私钥文件路径
this.ownPrivateKey = RSAUtil.getPrivateKeyFromPem(file);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 加密
*
* @param paramMap 加密参数
* @param jsonData 非加密参数
* @return 加密后的json
*/
public String encrypt(Map<String, String> paramMap, String jsonData) {
try {
String bizContent = new String(Base64.encodeBase64(RSAUtil.encrypt(jsonData.getBytes("UTF-8"), this.cPlusPublicKey)));
StringBuffer stringBuffer = new StringBuffer();
paramMap.put("bizcontent", bizContent);
Map<String, String> sortMap = new TreeMap<>(String::compareTo);
sortMap.putAll(paramMap);
for (Map.Entry<String, String> entry : sortMap.entrySet()) {
stringBuffer.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
String jsonStr = stringBuffer.substring(0, (stringBuffer.lastIndexOf("&")));
//加签privateKey是自己系统的私钥
String sign = new String(RSAUtil.sign(jsonStr.getBytes("UTF-8"), this.ownPrivateKey));
//加签信息放入请求中
paramMap.put("sign", sign);
} catch (IllegalBlockSizeException | BadPaddingException | IOException | InvalidKeyException | NoSuchAlgorithmException | SignatureException e) {
logger.error("加密异常");
}
return JacksonUtil.toJson(paramMap);
}
/**
* 解密
*
* @param paramMap 请求参数
*/
public String decrypt(Map<String, String> paramMap) {
return commonDecryptOperate(paramMap);
}
/**
* 解密
*
* @param request 请求
*/
public String decrypt(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
paramMap.put(key, request.getParameter(key));
}
return commonDecryptOperate(paramMap);
}
/**
* 解密
*
* @param request 请求
*/
public String decryptByJson(HttpServletRequest request) {
Map<String, String> paramMap = null;
try {
//接收数据
StringBuffer stringBuffer = new StringBuffer();
InputStream inputStream = request.getInputStream();
logger.info("========="+inputStream);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
logger.info("========="+inputStreamReader);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String jsonStr;
while ((jsonStr = bufferedReader.readLine()) != null) {
stringBuffer.append(jsonStr);
}
paramMap = JacksonUtil.fromJson(stringBuffer.toString(), Map.class);
} catch (Exception e) {
logger.error("获取请求参数异常");
return null;
}
return commonDecryptOperate(paramMap);
}
private String commonDecryptOperate(Map<String, String> paramMap) {
try {
String sign = paramMap.remove("sign");
StringBuffer stringBuffer = new StringBuffer();
Map<String, String> sortMap = new TreeMap<>(String::compareTo);
sortMap.putAll(paramMap);
for (Map.Entry<String, String> entry : sortMap.entrySet()) {
stringBuffer.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
String jsonStr = stringBuffer.substring(0, (stringBuffer.lastIndexOf("&")));
if (RSAUtil.verify(jsonStr.getBytes("UTF-8"), this.cPlusPublicKey, sign.getBytes("UTF-8"))) {
//if (true) {
String bizContent = paramMap.get("bizcontent");
return new String(RSAUtil.decrypt(Base64.decodeBase64(bizContent.getBytes("UTF-8")), this.ownPrivateKey));
}else{
logger.info("==================验签失败");
}
} catch (Exception ignored) {
logger.error("解密异常");
return null;
}
return null;
}
}
HttpClient
** /**
* 有参post请求,json交互
*/
public HttpResult doPostJson(String url, String json) {
HttpEntity entity = null;
try {
HttpPost httpPost = new HttpPost(url);
if (StringUtils.isNotBlank(json)) {
StringEntity stringEntity = new StringEntity(json);
stringEntity.setContentEncoding(Consts.UTF_8.toString());
stringEntity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
httpPost.setEntity(stringEntity);
}
CloseableHttpResponse response = client.execute(httpPost);
entity = response.getEntity();
HttpResult httpResult = new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(entity, Consts.UTF_8));
EntityUtils.consumeQuietly(entity);
return httpResult;
} catch (ParseException | UnsupportedEncodingException | ClientProtocolException e) {
e.printStackTrace();
} catch (SocketTimeoutException e) {
logger.error("HttpClient链接超时");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != entity) {
EntityUtils.consumeQuietly(entity);
}
}
return null;
}
**
配置
package com.sinosoft.newstandardcore.configuration.spring;
import com.sinosoft.newstandardcore.common.EncryptionDecryptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: Eric
**/
@Configuration
public class SpringCongifuration {
@Bean
public EncryptionDecryptionHandler cPlusEncryptionDecryptionHandler() {
return new EncryptionDecryptionHandler("***_public.cer", "own_private.pfx", "Kk123456");
}
测试
HttpResult httpResult = httpClientHandler.doPostJson("http://10.141.136.165:8080/NewCustomerService/newCustomerInfo", qaEncryptionDecryptionHandler.encrypt(paramMap,json));