AES和RSA加密的组合使用(一)
2016-07-21 本文已影响1406人
Dora404
关于接口安全加解密,做了一些联调,由于工作时间关系,一步步的将使用方法和技巧,实现步骤在简书上发布文章,记录下使用经验。加密步骤将按照下面目录持续更新:
- RAS的使用
- AES的使用
- Base64的使用
- 联调问题及解决方案
- AES与RAS的联合使用
一、 RAS非对称加密的使用
这里就略过了对RAS非对称加密的介绍,直接上代码:
Test类
- 客户端:加密使用公钥1(public_key_client)加密,私钥2(private_key_client)加签,解密使用私钥2验签,公钥1解密。
- 服务端 :解密使用公钥2(public_key_server)验签,私钥1(private_key_server)解密,加密使用公钥2加密,私钥1加签。
- 数据格式:明文为json格式,返回值也为json格式。
package rsa;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import com.google.gson.Gson;
/**
* 使用RSA实现服务端客户端加解密
* @author zhaofh 2016年7月6日
*@version 1.1
*/
public class EncryptionTest2 {
//客户端-公钥加密
public static String public_key_client = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCn6qfbrh5Or34H4tXEhDhIm30KcPIIWPdklRSK64rVS6Qx/oMqHFVDELzGTNcPaKw04kSbNHTwNQpumtPS/mZVPpTxeKg/jbDMkGYEoCNnVfgwZCBhpz6BxbSovTGWLzlmNBveG0cn1D+PT5vtQ0z40GCTEJIUd13W9UFhV9Yp+QIDAQAB";
//服务端-私钥解密
public static String private_key_server = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKfqp9uuHk6vfgfi1cSEOEibfQpw8ghY92SVFIrritVLpDH+gyocVUMQvMZM1w9orDTiRJs0dPA1Cm6a09L+ZlU+lPF4qD+NsMyQZgSgI2dV+DBkIGGnPoHFtKi9MZYvOWY0G94bRyfUP49Pm+1DTPjQYJMQkhR3Xdb1QWFX1in5AgMBAAECgYBtVdTArQpc79YfamsIz5Maa+wqTUq7drp3ir7aie5XXi5mwzNCyzoVNiPE9ymdhemDccV8TdbKxa6qQDQnEbEtSDdKSniqhSNho4oPHwb54V5M7JoKVElJqP8c4pYfCoC6KWA+2drv/DKEAiQp6EEeybcjMjJOxgjVqU0qfuC4UQJBANWp4/ixYv5XKx52esmHsx1RBxvzbYWu+vJLbav3TjHFBiMBW5/sE88RtvCcj3heNbaW1h3Fd5zhyPu+B3lKer0CQQDJMD5Q6FTu6qFlxXTrPRMYW9Kbfc9fYj7mmoOdx9/akK5axSo9h7yNanV+7gRD9uuvictKUTeavAHFizkdKb3tAkAF0BxyrKjL0KVMq96FUxrNZmHyIbpOE8eiBelS72SCOCEFnMjYXfzf+lRm0WuZ075UXGAw6Slq7D2ik7XyV9NlAkEAv2k24KMqq2RvyfPjGSwyTqqN5YH9GjLOxXecYTEYuUmNmK6dUY0ixyjSQMETLdZuxcPDtiEvVfgpd1jOLgDYwQJAJOTzJnufez1RCqd6+lPiy/4r9+CMUVoqRW+KJJAGUVzIMu16or6stxV8IBgXkjwnd5f9n+OTC6JFQewAWcurxw==";
//服务端-公钥验签
public static String public_key_server = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCDe3vhGZ4G2df78Jg8ECf9j/rvFUKDd1A8R4pZdS4pbpYOKuw2txOVzMTT1xiDZTGmq1o3cyzIc/QoYvwW77WZHOGIuMN9HUfV/E4UW6joWkFslwOHr6TNcrfsItQIYxHFOhQR8QjNCNNMpCZJngBzXQCX/xO9+rmypMgYBERMBQIDAQAB";
//客户端-私钥加签
public static String private_key_client = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIN7e+EZngbZ1/vwmDwQJ/2P+u8VQoN3UDxHill1Lilulg4q7Da3E5XMxNPXGINlMaarWjdzLMhz9Chi/BbvtZkc4Yi4w30dR9X8ThRbqOhaQWyXA4evpM1yt+wi1AhjEcU6FBHxCM0I00ykJkmeAHNdAJf/E736ubKkyBgEREwFAgMBAAECgYB3W1CgjmXDKFyGK+m2lKAI8XxFL1J7D4O5xOPVw6dNG0OCxvo5zUYtNX70I43wMZu6BlFWhup/aauaQglANQju/2euUghdy6ql4hG54rGXX95MtlqZYDWraps0WoZ25AXYApI/FhPDlNsFHtd1vSK4RdPCRAorogf713nWJkI2nQJBAP7Wnse4D7zLU2Q0X4K5DTNrXv+OVTIuJKg2vjODUTvIg+jZc9BcODNll6NMsaSl6V5gtne6cJybb1dbrHpnrlMCQQCEFOpN5HSZ/pIxnwb+pG6rn+oJhoFShxgeJgLo9v3BZECP/wJjA7pOXM8lEgMviirgzwh8IQQGpvh6u0HoBOFHAkEAulDZt7VHtEWHy6xK5D09fImU5A0BFvYLkPytJOZufuIEJzrM5Np3sIQnUJojCvjOXVUiMvkZmjY+OkVpHfktxwJAK73herpWA0nTkKth3aMHI79p+o2Y9oPW8OUVwaFKmGljGE0TtUbexGToFRbKB0xyttDZtoIYmztgvwSU5wn2sQJARVGajF3QY7pw28qwxczGrRPqqES4iiP98GVzFeNLZNuDQaXdY0t5z65LSBGH+/RMFbTfnW3xHy2Xq8PKZlj7TA==";
@Test
public void asda() throws Exception{
//Map传参并生成json
Map<String,Object> map =new HashMap<String,Object>();
map.put("userNo", "123456");
//URL
String url = "http://localhost:8080/T100040/weather/indexV";
//生成AES_KEY
String aesKey = AESCoder.initKey();
//参数加密格式: BASE64(RSA(AES_KEY))|AES(param)|RSA_SIGN(param)
String param =URLEncoder.encode(encrpytClient(new Gson().toJson(map),aesKey), "UTF-8");
//post请求
String encRsp = ConnectionUtil.sendPost(url,"param="+ param, "utf-8");
if(encRsp!=null&&!"".equals(encRsp)){
System.out.println(decryptClient(encRsp,aesKey));
}else{
}
}
/**
* <客户端>
* 公钥加密,私钥签名
* @author zhaofh 2016年7月6日 上午9:47:23
*
* @param param传输数据
* @return 返回结果(已加密)
*/
private static String encrpytClient(String param,String asekey){
try {
return encrpytClient(param.getBytes("utf-8"),asekey, public_key_client,private_key_client);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* <客户端>
* 数据加密,数据加签
* @author zhaofh 2016年7月6日 上午9:49:59
*
* @param data 报文明文
* @param aesKey 临时通信密钥
* @param selfPriKey 客户端私钥证书
* @param srvPubKey 服务端公钥证书
* @return 报文密文
*/
private static String encrpytClient(byte[] data,String aesKey, String public_key_param, String private_key_sign) throws Exception {
byte[] encoded_aesKey = RSAUtil.encryptByPublicKey(aesKey.getBytes(), public_key_param);
String encAesKey = RSAUtil.encryptBASE64(encoded_aesKey);
byte[] encData = AESCoder.encrypt(data, aesKey);
String outputStr = AESCoder.encryptBASE64(encData);
String sign = RSAUtil.sign(data, private_key_sign);
return encAesKey + "|" + outputStr + "|" + sign;
}
/**
* <客户端>
* 私钥解密,公钥验签
* @author zhaofh 2016年7月6日 上午9:49:59
*
* @param param传输数据
* @return 返回结果(已加密)
*/
private static String decryptClient(String param,String aesKey){
try {
return decryptClient(param,aesKey,public_key_client);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* <客户端>
* 数据解密,数据验签
* @author zhaofh 2016年7月6日 上午9:49:59
*
* @param data 报文密文
* @param aesKey 临时通信密钥
* @param srvPubKey 服务端公钥证书
* @return aesKey&&报文明文
*/
private static String decryptClient(String data, String aesKey, String public_key_sign) throws Exception {
String[] enc_data_seg = data.split("\\|");
byte[] encData = AESCoder.decrypt(AESCoder.decryptBASE64(enc_data_seg[0]),aesKey);
boolean status = RSAUtil.verify(encData, public_key_sign, enc_data_seg[1]);
if (status)
return new String(encData,"utf-8");
else
return null;
}
/**
* <服务端>
* 公钥加密,私钥签名
* @author zhaofh 2016年7月6日 上午9:47:23
*
* @param param传输数据
* @param aesKey AES_KEY
* @return 返回结果(已加密)
*/
public static String encrpytService(String param,String aesKey){
try {
return encrpytService(param.getBytes("utf-8"),aesKey, public_key_server,private_key_server);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* <服务端>
* 数据加密,数据加签
* @author zhaofh 2016年7月6日 上午9:49:59
*
* @param data 报文明文
* @param aesKey 临时通信密钥
* @param selfPriKey 客户端私钥证书
* @param srvPubKey 服务端公钥证书
* @return 报文密文
*/
private static String encrpytService(byte[] data,String aesKey, String public_key_param, String private_key_sign) throws Exception {
byte[] encData = AESCoder.encrypt(data, aesKey);
String outputStr = AESCoder.encryptBASE64(encData);
String sign = RSAUtil.sign(data, private_key_sign);
return outputStr + "|" + sign;
}
/**
* <服务端>
* 私钥解密,公钥验签
* @author zhaofh 2016年7月6日 上午9:49:59
*
* @param param传输数据
* @return 返回结果(已加密)
*/
public static String[] decryptService(String param){
try {
return decryptService(param,private_key_server,public_key_server);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* <服务端>
* 数据解密,数据验签
* @author zhaofh 2016年7月6日 上午9:49:59
*
* @param data 报文密文
* @param aesKey 临时通信密钥
* @param srvPubKey 服务端公钥证书
* @return aesKey&&报文明文
*/
private static String[] decryptService(String data, String private_key_param, String public_key_sign) throws Exception {
String[] enc_data_seg = data.split("\\|");
System.out.println("解密时数据:\n\r"+data);
byte[] encoded_aesKey =RSAUtil.decryptBASE64(enc_data_seg[0]);
byte[] decoded_aesKey =RSAUtil.decryptByPrivateKey(encoded_aesKey,private_key_param);
String aesKey =new String(decoded_aesKey);
byte[] encData = AESCoder.decrypt(AESCoder.decryptBASE64(enc_data_seg[1]),aesKey);
boolean status = RSAUtil.verify(encData, public_key_sign, enc_data_seg[2]);
if (status)
return new String[]{aesKey,new String(encData,"utf-8")};
else
return null;
}
}
RSAUtil类
- RSA的Util类网上种类甚是繁多,各种博客各种写法,为了更有效的开发,不出现联调时更多的附加问题和BUG,选用了《java加密与解密的艺术》里提供的加密方式,当然应用到自己的项目里还是要相应的做些改变和优化,比如说Base64选用,生成key和加密的算法选用等等。
package rsa;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
/**
* RSA安全编码组件
* @author 《java加密与解密的艺术 梁栋》
* zhaofh 2016-07-06 根据项目需求引用并优化
* @version 1.0
* @since 1.0
*/
public abstract class RSAUtil extends BaseCoder {
public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* 用私钥对信息生成数字签名
* @param data 加密数据
* @param privateKey 私钥
* @return
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
// 解密由base64编码的私钥
byte[] keyBytes = decryptBASE64(privateKey);
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 用私钥对信息生成数字签名
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
return encryptBASE64(signature.sign());
}
/**
* 校验数字签名
*
* @param data 加密数据
* @param publicKey 公钥
* @param sign 数字签名
* @return 校验成功返回true 失败返回false
* @throws Exception
*/
public static boolean verify(byte[] data, String publicKey, String sign)
throws Exception {
// 解密由base64编码的公钥
byte[] keyBytes = decryptBASE64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
// 验证签名是否正常
return signature.verify(decryptBASE64(sign));
}
/**
* 解密<br>
* 用私钥解密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String key)
throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 加密<br>
* 用公钥加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String key)
throws Exception {
// 对公钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
}
BaseCode接口
- 该接口主要用于Base64转码,以下代码仅作使用,上线时选择别的Base64转码方式,不要使用原生jdk的转码,后期会出一篇专门讲Base64的。
- 可作为工具接口 添加SHA MD5等算法。
package rsa;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* @author zhaofh 2016年7月6日
*
*/
public abstract class BaseCoder {
/**
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
/**
* BASE64加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
}
总结
以上内容为java服务端仅使用RSA两组密钥对进行双向的加解密过程(显示使用中不推荐双向,效率比较低,单项加密,返回值不需要加密或使用简单加密),仅作为java端使用,安卓和java还是有些许区别需要再做修改优化,记得关注以后内容哦。