跨服务请求中的数据加密与签名处理
数据隐私与安全是我们所有开发人员在业务需求开发过程中都避免不掉的问题, 比如数据防篡改, 隐秘信息加密, 用户登录ticket生成等场景, 然后这里我们就可以根据不同的具体业务场景选择合适的签名或者加密算法以达到我们的目的!
- 常用签名与加密算法介绍
- 业务场景分析
- 算法具体实现代码
- 其他
常用签名与加密算法介绍
消息摘要算法
- MD5: 消息摘要计算算法第五版,
1) 摘要长度固定: 任意长的原始数据, 经过MD5计算后, 得到的消息摘要长度都是固定的!
2) 抗修改: 对原始数据的任意一字节修改, 后续计算出来的MD5值都不同!
3) 通用性: 算法计算是基于字节的, 所以可以使用于字符串, 文件等需要签名的东西.
4) 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。 - SHA1withRSA: SHA1安全哈希算法, 用SHA1算法哈希值计算, 再用RSA进行加密
1) SHA1和MD5本是同根生, 都是从Md4发展而来, 用于计算消息摘要, 不过SHA1(20B)计算得到的摘要长度比MD5(16B)长一点, 会进一步提高抗攻击性, 同时计算性能也会下降一点;
2) SHA1withRSA在计算出安全摘要后, 再使用RSA进行分对称加密, 这样可以极高的防止由密文反推签名私钥的风险;
对称加密算法
- DES(Data Encryption Standard): 数据加密标准,是一种使用密钥加密的块算法
- 3DES: 顾名思义3重数据加密算法, 比DES更安全;
- AES: 高级加密标准, 加密算法与DES有本质的不同, 更高效与适用更多场景;
非对称加密算法
- RSA: 公开公钥密码体制, 使用公钥加密, 私钥私密保存用于解密加密后的密文信息;
业务场景分析
这里列举一些本人所遇到的数据加密场景及分析, 都是个人对问题的一些思考, 欢迎指正与交流
公司不同业务线间的服务调用
问题: (内网)公司内部有诸多的业务线, 时常就会有一些业务合作方面的需求, 涉及到不同服务间授权调用, 这里需要对于服务调用方, 一定要是被授权的基础上, 才能正常的调用服务提供方的接口服务, 同时也要保证请求方的数据不能被三方做任何修改;
方案: 使用SHA1withRSA, 可以有效的解决此问题;
支付公司的支付产品服务调用
问题: (公网)支付公司一般提供的支付产品接口, 支付授权肯定是要绝对保证的, 其次支付请求数据也不能被修改, 更甚者, 支付请求数据中的敏感信息需要做隐私加密, ;
方案: 涉及到了数据加密, 需要解密后还原数据的, 所以摘要算法就不能用了, 这里数据量小的话, 可以考虑非对称的加密算法RSA, 数据比较多的话, 还是推荐使用对称的加密算法, AES, DES, 提高效率;
SSO中用户登录后生成授权的 ticket
问题: 对用户的登录进行进行加密, 需要抗攻击, 同时因为每次请求都需要从ticket中解密出用户信息, 一般还会有ticket失效时间, 所以要高效的加解密效率;
方案: 可以采用对称的加密算法, 比如按块运算的AES, DES等;
支付服务调用时要对数据加密以及要保证数据的有效性
问题: 即需要保密数据, 又要防修改;
方案: 采用DES加密, 再使用SHA1withRSA对加密后的数据进行签名;
各个加解密算法具体实现代码
MD5 操作示例
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import me.gavincook.sso.share.enums.CoreEnum;
import me.gavincook.sso.share.exception.BizException;
/**
*
* 用于数据加密工具类
*
* @author Hinsteny
* @version $Id: MD5Utils.java, v0.0.1 2018-02-15 下午3:25 Hinsteny Exp $
*/
public class MD5Utils {
private static final String MD5 = "MD5";
/**
* 对文本进行32位小写MD5加密
*
* @param text
* @return 加密后的内容
*/
public static String calculateMD5Val(String text) {
try {
MessageDigest md5Encoder = MessageDigest.getInstance(MD5);
byte[] textBytes = text.getBytes();
md5Encoder.update(textBytes);
byte[] encodedBytes = md5Encoder.digest();
StringBuilder resultBuffer = new StringBuilder();
for (byte perByte : encodedBytes) {
int bt = perByte & 0xff;
if (bt < 16) {
resultBuffer.append(0);
}
resultBuffer.append(Integer.toHexString(bt));
}
return resultBuffer.toString();
} catch (NoSuchAlgorithmException e) {
throw new BizException(CoreEnum.NO_SUCH_ALGORITHM);
}
}
/**
* Test
* @param args
*/
public static void main(String[] args) {
String md5PrivateKey = UUID.randomUUID().toString();
String data = "走遍世界的心不能停...O(∩_∩)O哈哈~";
String sign = MD5Utils.calculateMD5Val(data + md5PrivateKey);
System.err.println(String.format("Data: %s, calculate MD5 to: %s", data + md5PrivateKey, sign));
String wrongSign = MD5Utils.calculateMD5Val(data + UUID.randomUUID().toString());
assert sign.equals(wrongSign) : "the sign is not correct";
}
}
SHA1withRSA 操作示例
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* <p>RSA签名,加解密处理,注意:密钥长度1024</p>
* @author Hinsteny
* @version $ID: RSASignUtils 2018-04-01 11:22 All rights reserved.$
*/
public class RSASignUtils {
/**
* 加密算法RSA
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* 签名算法
*/
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
public static enum Algorithm {
MD5withRSA("MD5withRSA"),
SHA1withRSA("SHA1withRSA"),
;
private String algorithm;
Algorithm(String algorithm) {
this.algorithm = algorithm;
}
public String getAlgorithm() {
return algorithm;
}
}
/**
* 获取公钥的key
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 获取私钥的key
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* <p>
* 生成密钥对(公钥和私钥)
* </p>
*
* @return KeyPair
*/
public static Map<String, Key> genKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
Key publicKey = keyPair.getPublic();
Key privateKey = keyPair.getPrivate();
Map<String, Key> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 签名字符串
*
* @param param 需要签名的数据
* @param privateKey 私钥(BASE64编码)
* @param charset 编码格式
* @return 签名结果(BASE64编码)
*/
public static String sign(Map<String, String> param, String privateKey, String charset) throws Exception {
return sign(Algorithm.SHA1withRSA, buildParam(param), privateKey, charset);
}
public static String sign(Algorithm algorithm, Map<String, String> param, String privateKey, String charset) throws Exception {
return sign(algorithm, buildParam(param), privateKey, charset);
}
/**
* 签名字符串
*
* @param text 需要签名的字符串
* @param privateKey 私钥(BASE64编码)
* @param charset 编码格式
* @return 签名结果(BASE64编码)
*/
public static String sign(String text, String privateKey, String charset) throws Exception {
return doSign(Algorithm.SHA1withRSA, text, privateKey, charset);
}
public static String sign(Algorithm algorithm, String text, String privateKey, String charset) throws Exception {
return doSign(algorithm, text, privateKey, charset);
}
/**
* 进行RSA签名
* @param algorithm
* @param text
* @param privateKey
* @param charset
* @return
* @throws Exception
*/
private static String doSign(Algorithm algorithm, String text, String privateKey, String charset) throws Exception {
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(algorithm.getAlgorithm());
signature.initSign(privateK);
signature.update(getContentBytes(text, charset));
byte[] result = signature.sign();
return Base64.encodeBase64String(result);
}
/**
* 验签
*
* @param text 需要签名的字符串
* @param sign 客户签名结果
* @param publicKey 公钥(BASE64编码)
* @param charset 编码格式
* @return 验签结果
*/
public static boolean verify(String text, String sign, String publicKey, String charset) throws Exception {
return verify(Algorithm.SHA1withRSA, text, sign, publicKey, charset);
}
public static boolean verify(Algorithm algorithm, String text, String sign, String publicKey, String charset) throws Exception {
return doVerify(algorithm, text, sign, publicKey, charset);
}
/**
* 进行RSA验签
* @param text
* @param sign
* @param publicKey
* @param charset
* @return
* @throws Exception
*/
private static boolean doVerify(Algorithm algorithm, String text, String sign, String publicKey, String charset) throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicK = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(algorithm.getAlgorithm());
signature.initVerify(publicK);
signature.update(getContentBytes(text, charset));
return signature.verify(Base64.decodeBase64(sign));
}
/**
* @param content origin content
* @param charset get byte encoding
* @return content bytes
*/
private static byte[] getContentBytes(String content, String charset) {
if (null == charset || "".equals(charset.trim())) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("sign encoding is not support:" + charset);
}
}
/**
* <p>
* 获取公钥
* </p>
*
* @param keyMap 密钥对
* @return public key
*/
public static String getPublicKey(Map<String, Key> keyMap) {
Key key = keyMap.get(PUBLIC_KEY);
return Base64.encodeBase64String(key.getEncoded());
}
/**
* <p>
* 获取私钥
* </p>
*
* @param keyMap 密钥对
* @return private key
*/
public static String getPrivateKey(Map<String, Key> keyMap) {
Key key = keyMap.get(PRIVATE_KEY);
return Base64.encodeBase64String(key.getEncoded());
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
private static String buildParam(Map<String, String> params) {
StringBuilder sbr = new StringBuilder("");
if (null != params && params.size() > 0) {
Map<String, String> sortMap = new TreeMap<>(String::compareTo);
sortMap.putAll(params);
final String KVItem = "%s=%s&";
sortMap.forEach((key, value) -> sbr.append(String.format(KVItem, key, value)));
sbr.setLength(sbr.length() - 1);
}
return sbr.toString();
}
/**
* test sign
* @param args params
*/
public static void main(String[] args) throws Exception {
String inputCharset = "UTF-8";
Map<String, Key> keyPair = RSASignUtils.genKeyPair();
String publicKey = RSASignUtils.getPublicKey(keyPair);
String privateKey = RSASignUtils.getPrivateKey(keyPair);
String data = "走遍世界的心不能停...O(∩_∩)O哈哈~";
// 默认使用 Algorithm.SHA1withRSA 签名算法
String sign = RSASignUtils.sign(data, privateKey, inputCharset);
System.out.println(String.format("Data: %s, sign to: %s", data, sign));
boolean verify = RSASignUtils.verify(data, sign, publicKey, inputCharset);
System.out.println(String.format("Use correct publicKey to verify, the result is: %s", verify));
String notCorrectPublicKey = RSASignUtils.getPublicKey(RSASignUtils.genKeyPair());
verify = RSASignUtils.verify(data, sign, notCorrectPublicKey, inputCharset);
System.out.println(String.format("Use wrong publicKey to verify, the result is: %s", verify));
// 指定使用 Algorithm.MD5withRSA 签名算法
sign = RSASignUtils.sign(Algorithm.MD5withRSA, data, privateKey, inputCharset);
System.out.println(String.format("Data: %s, sign to: %s", data, sign));
verify = RSASignUtils.verify(Algorithm.MD5withRSA, data, sign, publicKey, inputCharset);
System.out.println(String.format("Use correct publicKey to verify, the result is: %s", verify));
notCorrectPublicKey = RSASignUtils.getPublicKey(RSASignUtils.genKeyPair());
verify = RSASignUtils.verify(Algorithm.MD5withRSA, data, sign, notCorrectPublicKey, inputCharset);
System.out.println(String.format("Use wrong publicKey to verify, the result is: %s", verify));
}
}
3DES 操作示例
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* DES加解密算法工具
* 加密用的Key 可以用26个字母和数字组成,最好不要用保留字符
* 此处使用DES-56-CBC加密模式,key需要为8位。
* 此处使用DESede-168-CBC加密模式,key需要为24位。
* @author Hinsteny
* @version $ID: DESUtils 2018-02-25 All rights reserved.$
*/
public class DESUtils {
/** 编码类型 */
private static final String CHARCODE = "UTF-8";
/** 默认的DES秘钥长度 **/
private static final int DEFAULT_DES_KEY_LENGTH = 56;
/** 默认的3DES秘钥长度 **/
private static final int DEFAULT_3DES_KEY_LENGTH = 168;
/**
* 使用CBC模式,需要一个向量iv,可增加加密算法的强度
*/
private static final String MODEL = "12345678";
/** 算法 */
private static final String DES = "DES";
private static final String[] algorithms = {"DES", "DESede"};
/** [DES] 算法/模式/补码方式 */
private static final String ALGORITHMDES = "DES/CBC/PKCS5Padding";
/** [3DES] 算法/模式/补码方式 */
private static final String ALGORITHM3DES = "DESede/CBC/PKCS5Padding";
private static final Map<String, String> ALGORITHMS_MAP;
static {
Map<String, String> data = new HashMap<>();
data.put(algorithms[0], ALGORITHMDES);
data.put(algorithms[1], ALGORITHM3DES);
ALGORITHMS_MAP = Collections.unmodifiableMap(data);
}
/**
* 生成一个AES加密私钥串
* @return
*/
public static String generateDESKey() {
return generateDESKey(algorithms[0], DEFAULT_DES_KEY_LENGTH);
}
/**
* 生成一个AES加密私钥串
* @return
*/
public static String generate3DESKey() {
return generateDESKey(algorithms[1], DEFAULT_3DES_KEY_LENGTH);
}
/**
*
* @param keyLen AES秘钥长度可选值有
* @return
*/
private static String generateDESKey(String algorithm, int keyLen) {
if (!(DEFAULT_DES_KEY_LENGTH == keyLen || DEFAULT_3DES_KEY_LENGTH == keyLen)) {
throw new RuntimeException("DES key length is not correct");
}
KeyGenerator kg = null;
try {
kg = KeyGenerator.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
kg.init(keyLen);
SecretKey sk = kg.generateKey();
byte[] b = sk.getEncoded();
return byteToHexString(b);
}
/**
* 判断key长度有效性
* @param needKeyLen
* @return
*/
private static boolean judgeKey(String key, int needKeyLen) {
if (key == null || "".equals(key.trim())) {
throw new RuntimeException("DES key is not valid");
}
if (!(needKeyLen == key.length())) {
throw new RuntimeException("DES key length is not correct");
}
return true;
}
/**
* byte数组转化为16进制字符串
* @param bytes
* @return
*/
public static String byteToHexString(byte[] bytes){
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
int source = (bytes[i] & 0xFF);
String strHex=Integer.toHexString(source);
if(strHex.length() < 2){
sb.append(strHex);
} else {
sb.append(strHex.charAt(0));
}
}
return sb.toString();
}
/**
* 加密
*
* @param src 需要加密的内容
* @param key 加密秘钥
* @return
*/
public static String encrypt(String src, String key) {
judgeKey(key, 8);
return encrypt(src, key, algorithms[0], CHARCODE);
}
/**
* 加密
*
* @param src 需要加密的内容
* @param key 加密秘钥
* @return
*/
public static String encrypt3DES(String src, String key) {
judgeKey(key, 24);
return encrypt(src, key, algorithms[1], CHARCODE);
}
/**
* 解密
*
* @param src 密文
* @param key 密钥
* @return
*/
public static String decrypt(String src, String key) {
judgeKey(key, 8);
return decrypt(src, key, algorithms[0], CHARCODE);
}
/**
* 解密
*
* @param src 密文
* @param key 密钥
* @return
*/
public static String decrypt3DES(String src, String key) {
judgeKey(key, 24);
return decrypt(src, key, algorithms[1], CHARCODE);
}
/**
* 加密
*
* @param src 需要加密的内容
* @param key 加密秘钥
* @param algorithm 加密所用算法
* @param charset 编码
* @return
*/
private static String encrypt(String src, String key, String algorithm, String charset) {
String afterCode;
try {
byte[] raw = key.getBytes();
Cipher cipher = Cipher.getInstance(ALGORITHMS_MAP.get(algorithm));
SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
IvParameterSpec iv = new IvParameterSpec(MODEL.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(src.getBytes(charset));
//此处使用BASE64做转码功能,防止中文乱码
afterCode = new BASE64Encoder().encode(encrypted);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
return afterCode.replaceAll("\r|\n", "");
}
/**
* 解密
*
* @param src 密文
* @param key 密钥
* @return
*/
private static String decrypt(String src, String key, String algorithm, String charset) {
String originalString;
try {
byte[] raw = key.getBytes(CHARCODE);
SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
Cipher cipher = Cipher.getInstance(ALGORITHMS_MAP.get(algorithm));
IvParameterSpec iv = new IvParameterSpec(MODEL.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先用base64解密
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(src);
byte[] original = cipher.doFinal(encrypted1);
originalString = new String(original, charset);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
return originalString;
}
/**
* test
* @param args
*/
public static void main(String[] args) {
String data = "走遍世界的心不能停...O(∩_∩)O哈哈~";
String key;
String secret;
key = DESUtils.generateDESKey();
System.out.println("key is : " + key);
secret = DESUtils.encrypt(data, key);
System.out.println(String.format("encrypt data: %s, result: %s", data, secret));
String dData = DESUtils.decrypt(secret, key);
System.out.println("decrypt result: " + dData);
key = DESUtils.generate3DESKey();
System.out.println("key is : " + key);
secret = DESUtils.encrypt3DES(data, key);
System.out.println(String.format("encrypt data: %s, result: %s", data, secret));
dData = DESUtils.decrypt3DES(secret, key);
System.out.println("decrypt result: " + dData);
}
}
AES 操作示例
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
/**
* AES加解密算法
* 加密用的Key 可以用26个字母和数字组成,最好不要用保留字符
* 此处使用AES-128-CBC加密模式,key需要为16位。
* @author Hinsteny
* @version $Id: AESUtils.java, v0.0.1 2018-02-20 上午7:52 Hinsteny Exp $
*/
public class AESUtils {
/** 默认的AES秘钥长度 **/
private static final int DEFAULT_KEY_LENGTH = 128;
/** 算法 */
private static final String AES = "AES";
/** 算法/模式/补码方式 */
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
/** 编码类型 */
private static final String CHARCODE = "UTF-8";
/** 使用CBC模式,需要一个向量iv,可增加加密算法的强度 */
private static final String MODEL = "0102030405060708";
/**
* 生成一个AES加密私钥串
* @return
*/
public static String generateAESKey() {
return generateAESKey(DEFAULT_KEY_LENGTH);
}
/**
*
* @param keyLen AES秘钥长度可选值有
* @return
*/
private static String generateAESKey(int keyLen) {
if (!(keyLen == 128)) {
throw new RuntimeException("AES key length is not correct");
}
KeyGenerator kg = null;
try {
kg = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
kg.init(keyLen);
SecretKey sk = kg.generateKey();
byte[] b = sk.getEncoded();
return byteToHexString(b);
}
/**
* 判断key长度有效性
* @param keyLen
* @return
*/
private static boolean judgeKeyLen(int keyLen) {
if (!(keyLen == 16)) {
throw new RuntimeException("AES key length is not correct");
}
return true;
}
/**
* byte数组转化为16进制字符串
* @param bytes
* @return
*/
public static String byteToHexString(byte[] bytes){
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
int source = (bytes[i] & 0xFF);
String strHex=Integer.toHexString(source);
if(strHex.length() < 2){
sb.append(strHex);
} else {
sb.append(strHex.charAt(0));
}
}
return sb.toString();
}
/**
* 加密
*
* @param src 需要加密的内容
* @param key 加密秘钥
* @return
*/
public static String encrypt(String src, String key) {
return encrypt(src, key, CHARCODE);
}
/**
* 解密
*
* @param src 密文
* @param key 密钥
* @return
*/
public static String decrypt(String src, String key) {
return decrypt(src, key, CHARCODE);
}
/**
* 加密
*
* @param src 需要加密的内容
* @param key 加密秘钥
* @param charset 编码
* @return
*/
public static String encrypt(String src, String key, String charset) {
if (key == null) {
return null;
}
judgeKeyLen(key.length());
String afterCode;
try {
byte[] raw = key.getBytes();
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
IvParameterSpec iv = new IvParameterSpec(MODEL.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(src.getBytes(charset));
//此处使用BASE64做转码功能,防止中文乱码
afterCode = new BASE64Encoder().encode(encrypted);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
return afterCode.replaceAll("\r|\n", "");
}
/**
* 解密
*
* @param src 密文
* @param key 密钥
* @return
*/
public static String decrypt(String src, String key, String charset) {
// 判断Key密钥是否正确
if (key == null) {
return null;
}
judgeKeyLen(key.length());
String originalString;
try {
byte[] raw = key.getBytes(CHARCODE);
SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
Cipher cipher = Cipher.getInstance(ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(MODEL.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先用base64解密
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(src);
byte[] original = cipher.doFinal(encrypted1);
originalString = new String(original, charset);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
return originalString;
}
/**
* test
* @param args
*/
public static void main(String[] args) {
String data = "走遍世界的心不能停...O(∩_∩)O哈哈~";
String key = "";
String secret = "";
key = AESUtils.generateAESKey(192);
System.out.println("key length 128: " + key);
secret = AESUtils.encrypt(data, key);
System.out.println(String.format("encrypt data: %s, result: %s", data, secret));
String dData = AESUtils.decrypt(secret, key);
System.out.println("decrypt result: " + dData);
}
}
RSA 加密操作示例
import org.hisoka.commons.ByteUtils;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* RSA 加解密工具
* PS:
* 1. 公钥216位, 私钥844位
* 2. 此工具没有实现超长数据分片加密功能, 因此被加密的数据不超过117位
* @author Hinsteny
* @version $ID: RSAUtils 2018-04-15 15:03 All rights reserved.$
*/
public class RSAUtils {
/**
* 签名算法
*/
public static final String ALGORITHM = "RSA";
/**
* 算法实现
*/
public static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";
/**
* <p>
* 生成密钥对(公钥和私钥)
* </p>
*
* @return KeyPair
*/
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
return keyPair;
}
/**
* <p>
* 获取公钥
* </p>
*
* @param keyPair 密钥对
* @return public key
*/
public static String getPublicKey(KeyPair keyPair) {
Key key = keyPair.getPublic();
return Base64.getEncoder().encodeToString(key.getEncoded());
}
/**
* <p>
* 获取私钥
* </p>
*
* @param keyPair 密钥对
* @return private key
*/
public static String getPrivateKey(KeyPair keyPair) {
Key key = keyPair.getPrivate();
return Base64.getEncoder().encodeToString(key.getEncoded());
}
/**
* 使用给定key, 对字符串数据按照指定编码, 进行加密
* @param key
* @param data
* @param charset
* @return
* @throws Exception
*/
public static String encryptData(String key, String data, String charset) throws Exception {
return encryptData(key, data, charset, true);
}
public static String encryptData(String key, String data, String charset, boolean usePubKey) throws Exception {
byte[] dataBytes = data.getBytes(charset);
byte[] encryptData = encryptData(key, dataBytes, usePubKey);
return ByteUtils.byteToHex(encryptData);
}
public static byte[] encryptData(String key, byte[] content, boolean usePubKey) throws Exception {
return encrypt(content, buildKey(key, usePubKey));
}
/**
* 使用给定key, 对字符串数据按照指定编码, 进行解密
* @param key
* @param data
* @param charset
* @return
* @throws Exception
*/
public static String decryptData(String key, String data, String charset) throws Exception {
return decryptData(key, data, charset, false);
}
public static String decryptData(String key, String data, String charset, boolean usePubKey) throws Exception {
byte[] dataBytes = ByteUtils.hexToyte(data.getBytes(charset));
byte[] encryptData = decryptData(key, dataBytes, usePubKey);
return new String(encryptData, charset);
}
public static byte[] decryptData(String key, byte[] content, boolean usePubKey) throws Exception {
return decrypt(content, buildKey(key, usePubKey));
}
/**
* [公钥/私钥]加密
* @param content
* @param key[PublicKey/PrivateKey]
* @return
* @throws Exception
*/
private static byte[] encrypt(byte[] content, Key key) throws Exception {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(content);
}
/**
* [公钥/私钥]解密
* @param content
* @param key[PublicKey/PrivateKey]
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] content, Key key) throws Exception {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(content);
}
/**
* 根据原始密钥串构建key对象
* @param key
* @param isPubKey
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private static Key buildKey(String key, boolean isPubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keyByte = Base64.getDecoder().decode(key);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
Key generateKey;
if (isPubKey) {
KeySpec keySpec = new X509EncodedKeySpec(keyByte);
generateKey = keyFactory.generatePublic(keySpec);
} else {
KeySpec keySpec = new PKCS8EncodedKeySpec(keyByte);
generateKey = keyFactory.generatePrivate(keySpec);
}
return generateKey;
}
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateKeyPair();
String publicKey = RSASignUtils.getPublicKey(keyPair);
String privateKey = RSASignUtils.getPrivateKey(keyPair);
System.out.println("Public key: " + publicKey);
System.out.println("Private key: " + privateKey);
String data = "走遍世界的心不能停...O(∩_∩)O哈哈~", secret, origin;
System.out.println("============== 公钥加密, 私钥解密 ==============");
System.out.println("Origin data before encrypt is: " + data);
secret = RSAUtils.encryptData(publicKey, data, "UTF-8");
System.out.println("secret data after encrypt is: " + secret);
origin = RSAUtils.decryptData(privateKey, secret, "UTF-8");
System.out.println("origin data after encrypt is: " + origin);
System.out.println("============== 私钥加密, 公钥解密 ==============");
System.out.println("Origin data before encrypt is: " + data);
secret = RSAUtils.encryptData(privateKey, data, "UTF-8", false);
System.out.println("secret data after encrypt is: " + secret);
origin = RSAUtils.decryptData(publicKey, secret, "UTF-8", true);
System.out.println("origin data after encrypt is: " + origin);
}
}
/**
* @author Hinsteny
* @date 2018-04-15
* @since 1.0.0
*/
public class ByteUtils {
/**
* translate bytes arrays to hex-string
* @param bytes
* @return
*/
public static String byteToHex(byte[] bytes) {
StringBuilder hex = new StringBuilder();
String temp;
for (byte i : bytes) {
temp = Integer.toHexString(i & 0xFF);
if (temp.length() == 1) {
temp = "0" + temp;
}
hex.append(temp);
}
return hex.toString().toUpperCase();
}
/**
* translate hex-bytes to bytes
* @param data
* @return
*/
public static byte[] hexToyte(byte[] data) {
if (null == data || data.length % 2 != 0) {
throw new IllegalArgumentException("Illegal parameters");
}
byte[] bytes = new byte[data.length / 2];
String temp;
for (int n = 0; n < data.length; n+=2) {
temp = new String(data, n, 2);
bytes[n/2] = (byte) Integer.parseInt(temp, 16);
}
return bytes;
}
}