网站建设需要基础吗江门搜狗网站推广优化
前后端交互
- 通信请求使用https
- 对请求参数进行签名,防止数据篡改
- 对请求参数以及响应数据进行加解密
- app中使用ssl pinning防止抓包操作
https协议
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-78n9M2PH-1677252127361)(安全.assets/https加密流程.jpg)]
签名参数
加签和验签:发送方将请求参数通过加密算法生成一个sign值,放到请求参数里;接收方收到请求后,使用同样的方式对请求参数进行加密得到一个sign值,只要两个sign值相同,就说明参数没有被篡改。
加签操作步骤:
- 将所有参数(除了sign本身,以及值是空的参数)按参数键字母升序排序。
- 然后把排序后的参数按 参数1值1 参数2值2 … 参数n值n 的方式拼接成一个字符串。
- 在上一步得到的字符串前面加上密钥key,然后计算md5值,得到32位字符串,然后转成大写,得到的字符串作为sign的值放到请求参数里。
验签操作步骤:
同 加签操作步骤
现在假设需要传输的数据:/guest/rechargeNotify?p2=v2&p1=v1&method=cancel&p3=&pn=vn(实际情况最好是通过post方式发送)
- 拼接字符串,首先去除值是空的参数p3,剩下p2=v2&p1=v1&method=cancel&pn=vn,然后按参数名字符升序排序得到字符串:method=cancel&p1=v1&p2=v2&pn=vn。
- 然后做参数名和值的拼接,最后得到methodcancelp1v1p2v2pnvn。
- 在上面拼接得到的字符串前面加上验证密钥key,假设是abc,得到新的字符串abcmethodcancelp1v1p2v2pnvn。
- 将上面得到的字符串进行md5计算,假设得到的是abcdef,然后转为大写,得到ABCDEF这个值即为sign签名值。最终产生的url应该如下:/guest/rechargeNotify?p2=v2&p1=v1&method=cancel&p3=&pn=vn&sign=ABCDEF
MD5加密:
public String getSignMD5(String param, String sign) {StringBuffer str = new StringBuffer(sign);if(StringUtils.isNotEmpty(param)){JSONObject obj = JSONObject.parseObject(param);List<String> keys = new ArrayList<String>(obj.keySet());keys.remove(ApiSignUtil.SIGN);Collections.sort(keys);for (String key : keys) {Object value = obj.get(key);if (value == null) {value = "";}str.append(value);}}return getMd5(str.toString());
}public String getMd5(String plainText) {try {MessageDigest md = MessageDigest.getInstance("MD5");md.update(plainText.getBytes());byte[] b = md.digest();StringBuilder buf = new StringBuilder();byte[] var5 = b;int var6 = b.length;for(int var7 = 0; var7 < var6; ++var7) {byte aB = var5[var7];int i = aB;if (aB < 0) {i = aB + 256;}if (i < 16) {buf.append("0");}buf.append(Integer.toHexString(i));}return buf.toString();} catch (NoSuchAlgorithmException var9) {var9.printStackTrace();return "";}
}
加解密请求响应数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bfRrbNux-1677252127362)(安全.assets/1648114154350.png)]
前后端交互常用的通信加密方案:
客户端:
- 生成AES密钥,并保存AES密钥;
- 用AES密钥对请求传输数据进行加密;
- 使用RSA公钥对AES密钥加密,然后把值放到自定义的请求头;
- 向服务器发起请求;
服务端:
- 拿到自定义的请求头,用RSA私钥解密,得到AES密钥;
- 使用AES密钥对请求数据进行解密;
- 使用AES密钥对响应数据进行加密;
- 向客户端发送响应;
AES对称加密
AES加解密总共有以下这些
算法/模式/填充 字节加密后数据长度 不满16字节加密后长度
AES/CBC/NoPadding 16 不支持
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Padding 32 16
AES/CFB/NoPadding 16 原始数据长度
AES/CFB/PKCS5Padding 32 16
AES/CFB/ISO10126Padding 32 16
AES/ECB/NoPadding 16 不支持
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/OFB/NoPadding 16 原始数据长度
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支持
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16
AES/ECB
- 不带模式和填充来获取AES算法的时候,其默认使用AES/ECB/PKCS5Padding(输入可以不是16字节,也不需要向量)
Cipher cipher = Cipher.getInstance("AES");
AES/ECB/PKCS5Padding
String content = "在线助手";// 生成密钥需要的密码值String key = "www.it399.com";/*** AES加密方式一:AES不指定模式和填充,默认为 ECB/PKCS5Padding** 不能使用填充向量* java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV*/System.out.println("【0】AES不指定模式和填充,默认为 ECB/PKCS5Padding,输入可以不是16字节,也不需要填充向量\n");//128byte[] encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_DEFAULT);encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_DEFAULT);//192encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_DEFAULT);encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_DEFAULT);//256encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_DEFAULT);encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_DEFAULT);
AES/CBC
- 输入必须是16字节,不然报错 javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
- CBC模式必须提供初始向量IvParameterSpec,不然报错 java.security.InvalidKeyException: Parameters missing
content: 在线助手
key: www.it399.com111
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytesat com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1041)at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:1009)at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)at javax.crypto.Cipher.doFinal(Cipher.java:2165)at com.csy.spring.it399.controller.encode.aes.AESUtil.encrypt(AESUtil.java:80)at com.csy.spring.it399.controller.encode.aes.AESUtil.main(AESUtil.java:200)
java.security.InvalidKeyException: Parameters missingat com.sun.crypto.provider.CipherCore.init(CipherCore.java:470)at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313)at javax.crypto.Cipher.implInit(Cipher.java:802)at javax.crypto.Cipher.chooseProvider(Cipher.java:864)at javax.crypto.Cipher.init(Cipher.java:1249)at javax.crypto.Cipher.init(Cipher.java:1186)at com.csy.spring.it399.controller.encode.aes.AESUtil.decrypt(AESUtil.java:117)at com.csy.spring.it399.controller.encode.aes.AESUtil.main(AESUtil.java:202)
encode: null
decode: null
初始化加密模式的时改成
Cipher cipher = Cipher.getInstance(“AES/CBC/NoPadding”);
java.security.InvalidKeyException: Parameters missing解决办法:
if (modeAndPadding.equals(EncodeType.AES_CBC_NoPadding)) {//指定一个初始化向量 (Initialization vector,IV), IV 必须是16位cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(getIV()));
} else {cipher.init(Cipher.ENCRYPT_MODE, keySpec);
}
AES/CBC/NoPadding, AES/CBC/PKCS5Padding, AES/CBC/ISO10126Padding
/*** 1.1 AES/CBC* AES/CBC/NoPadding* AES/CBC/PKCS5Padding* AES/CBC/ISO10126Padding*/
System.out.println("【1.1】AES_CBC_NoPadding模式");
content = "在线助手在线助手在线助手在线助手";
key = "www.it399.com";
encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
AES/CFB
-
需要向量,不然报如下错误
java.security.InvalidKeyException: Parameters missing
-
AES/CFB/NoPadding,AES/CFB/PKCS5Padding,AES/CFB/ISO10126Padding
/*** 1.2 AES/CFB* AES/CBC/NoPadding* AES/CBC/PKCS5Padding* AES/CBC/ISO10126Padding*/ System.out.println("【1.2】AES_CFB_NoPadding模式\n"); content = "在线助手"; // 生成密钥需要的密码值 key = "https://www.it399.com"; encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding); encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding); encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding); encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding); encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding); encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
AES/ECB
-
AES/ECB不要填充变量,不然会报如下错误
java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
AES/ECB/NoPadding,AES/ECB/PKCS5Padding,AES/ECB/ISO10126Padding
/*** 1.3 AES/ECB* AES/ECB/NoPadding* AES/ECB/PKCS5Padding* AES/ECB/ISO10126Padding*/System.out.println("【1.3】AES_ECB模式");content = "在线助手";// 生成密钥需要的密码值key = "https://www.it399.com";encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);
完整代码
AESUtil .java
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/*** 在线助手|在线工具|在线生成|在线制作* https://www.it399.com/* 在线助手博客* https://www.it399.com/blog/index*/
public class AESUtil {public static final String CHARSET = "UTF-8";private static byte[] encryptOrDecrypt(int mode,byte[] byteContent, String key,byte[] iv, AESType type, String modeAndPadding) throws InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {KeyGenerator kgen = KeyGenerator.getInstance("AES");//此处解决mac,linux报错SecureRandom random = SecureRandom.getInstance("SHA1PRNG");random.setSeed(key.getBytes());kgen.init(type.value, random);SecretKey secretKey = kgen.generateKey();byte[] enCodeFormat = secretKey.getEncoded();SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, "AES");Cipher cipher = Cipher.getInstance(modeAndPadding);// 创建密码器if ( null !=iv ) {//指定一个初始化向量 (Initialization vector,IV), IV 必须是16位cipher.init(mode, keySpec, new IvParameterSpec(iv));} else {cipher.init(mode, keySpec);}byte[] result = cipher.doFinal(byteContent);return result;}public static void main(String[] args) throws Exception {
// System.out.println("【1】AES不指定模式和填充,默认为 ECB/PKCS5Padding,输入可以不是16字节,也不需要填充向量\n");
// // 需要加密的内容
// String content = "在线助手";
// // 生成密钥需要的密码值
// String key = "www.it399.com111";
// System.out.println("content: " + content + "\nkey: " + key);
// byte[] encodeByte;
// byte[] decodeByte;
// //默认方式 每次加密都不一样,但是秘钥是一样的,所以解密还是一样的
// // 内容加密后的值
// encodeByte = encrypt(content.getBytes(CHARSET), key, AESType.AES_128, EncodeType.AES_DEFAULT);
// String encodeStr = TypeConvert.bytesToHexString(encodeByte);
// // 被加密的内容解密后的值
// decodeByte = decrypt(encodeByte, key, AESType.AES_128, EncodeType.AES_DEFAULT);
// String decodeStr = new String(decodeByte,CHARSET);
// System.out.println("encode: " + encodeStr + "\ndecode: " + decodeStr);
//
//
// System.out.println("【2】AES_CBC_NoPadding模式,输入必须是16*n字节,需要填充向量\n");
// // 内容加密后的值
// //待加密内容不足16*n位 报错javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
// //需要填充向量,不然报错java.security.InvalidKeyException: Parameters missing
// //得到加密后的内容先base64编码再解码再传给解码,不然直接转回乱码
// content = "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈";
// encodeByte = encrypt(content.getBytes(CHARSET), key, AESType.AES_256, EncodeType.AES_CBC_NoPadding);
// encodeStr = TypeConvert.bytesToHexString(encodeByte);
// decodeByte = decrypt(TypeConvert.hexStringToBytes(encodeStr), key, AESType.AES_256, EncodeType.AES_CBC_NoPadding);
// decodeStr = new String(decodeByte,CHARSET);
// System.out.println("encode: " + encodeStr + "\ndecode: " + decodeStr);String content = "在线助手";// 生成密钥需要的密码值String key = "www.it399.com";byte[] encrypt;/*** AES加密方式一:AES不指定模式和填充,默认为 ECB/PKCS5Padding*/
// System.out.println("【0】AES不指定模式和填充,默认为 ECB/PKCS5Padding,输入可以不是16字节,也不需要填充向量\n");
// //128
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_DEFAULT);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_DEFAULT);
// //192
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_DEFAULT);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_DEFAULT);
// //256
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_DEFAULT);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_DEFAULT);
// /**
// * 1.1 AES/CBC (需要填充向量16*n)
// * AES/CBC/NoPadding
// * AES/CBC/PKCS5Padding
// * AES/CBC/ISO10126Padding
// */
// System.out.println("【1.1】AES_CBC_NoPadding模式,需要填充向量,待加密必须是16*n");
// content = "在线助手在线助手在线助手在线助手";
// key = "www.it399.com";
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
// /**
// * 1.2 AES/CFB
// * AES/CBC/NoPadding
// * AES/CBC/PKCS5Padding
// * AES/CBC/ISO10126Padding
// */
// System.out.println("【1.2】AES_CFB_NoPadding模式\n");
// content = "在线助手";
// // 生成密钥需要的密码值
// key = "https://www.it399.com";
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
// /**
// * 1.2 AES/ECB
// * AES/ECB/NoPadding
// * AES/ECB/PKCS5Padding
// * AES/ECB/ISO10126Padding
// */
// System.out.println("【1.3】AES_ECB模式");
// content = "在线助手";
// // 生成密钥需要的密码值
// key = "https://www.it399.com";
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);/*** 1.4 AES/OFB* AES/OFB/NoPadding* AES/OFB/PKCS5Padding* AES/OFB/ISO10126Padding*/System.out.println("【1.4】AES_OFB模式");content = "在线助手";// 生成密钥需要的密码值key = "https://www.it399.com";encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_OFB_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_OFB_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_OFB_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_OFB_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_OFB_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_OFB_PKCS5Padding);/*** 1.5 AES/PCBC* AES/PCBC/NoPadding* AES/PCBC/PKCS5Padding* AES/PCBC/ISO10126Padding*/System.out.println("【1.5】AES_PCBC模式");content = "在线助手";// 生成密钥需要的密码值key = "https://www.it399.com";encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_PCBC_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_PCBC_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_PCBC_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_PCBC_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_PCBC_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_PCBC_PKCS5Padding);//
// /**1.3 AES/CBC
// * AES_CBC_NoPadding模式(填充向量可选)
// */System.out.println("【1.3】AES_CBC_NoPadding模式");content = "在线助手在线助手在线助手在线助手";// 生成密钥需要的密码值key = "www.it399.com";encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);content = "在线助手";// 生成密钥需要的密码值key = "www.it399.com";encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
//
//
//
// /**
// * 2.1 AES/CFB 128/192/256位加解密
// * AES_CFB_NoPadding模式(填充向量可选)
// */
// System.out.println("【2.1】AES_CFB_NoPadding模式,需要填充向量\n");
// content = "在线助手";
// // 生成密钥需要的密码值
// key = "www.it399.com";
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_CFB_NoPadding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_CFB_NoPadding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_CFB_NoPadding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_CFB_NoPadding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_CFB_NoPadding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_CFB_NoPadding);
//
// /**
// * 2.2 AES/CFB
// * AES_CFB_NoPadding模式(填充向量可选)
// */
// System.out.println("【2.2】AES_CFB_NoPadding模式\n");
// content = "在线助手";
// // 生成密钥需要的密码值
// key = "www.it399.com";
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
//
// /**2.3 AES/CFB
// * AES_CFB_NoPadding模式(填充向量可选)
// */
// System.out.println("【2.3】AES_CFB_NoPadding模式\n");
// content = "在线助手";
// // 生成密钥需要的密码值
// key = "www.it399.com";
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_CFB_ISO10126Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_CFB_ISO10126Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_CFB_ISO10126Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_CFB_ISO10126Padding);
// encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_CFB_ISO10126Padding);
// encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_CFB_ISO10126Padding);}/**** @param isEncrypt* @param source* @param key* @param type* @param encodeType*/public static byte[] encryptOrdecrypt(boolean isEncrypt,byte[] source,String key,byte[] iv,AESType type,String encodeType) throws UnsupportedEncodingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {if (isEncrypt){byte[] encodeByte = encryptOrDecrypt(Cipher.ENCRYPT_MODE,source,key,iv,type,encodeType);String encodeStr = TypeConvert.bytesToHexString(encodeByte);return encodeByte;}else{byte[] decodeByte = encryptOrDecrypt(Cipher.DECRYPT_MODE,source, key,iv,type, encodeType);String decodeStr = new String(decodeByte,CHARSET);return decodeByte;}}/*** 指定一个初始化向量 (Initialization vector,IV),IV 必须是16位*/public static final byte[] getIV() throws Exception {return "1234567812345678".getBytes(CHARSET);}
EncodeType
/*** 在线助手|在线工具|在线生成|在线制作* https://www.it399.com/* 在线助手博客* https://www.it399.com/blog/index*/
public class EncodeType {
// 算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
// AES/CBC/NoPadding 16 不支持
// AES/CBC/PKCS5Padding 32 16
// AES/CBC/ISO10126Padding 32 16
// AES/CFB/NoPadding 16 原始数据长度
// AES/CFB/PKCS5Padding 32 16
// AES/CFB/ISO10126Padding 32 16
// AES/ECB/NoPadding 16 不支持
// AES/ECB/PKCS5Padding 32 16
// AES/ECB/ISO10126Padding 32 16
// AES/OFB/NoPadding 16 原始数据长度
// AES/OFB/PKCS5Padding 32 16
// AES/OFB/ISO10126Padding 32 16
// AES/PCBC/NoPadding 16 不支持
// AES/PCBC/PKCS5Padding 32 16
// AES/PCBC/ISO10126Padding 32 16//默认为 ECB/PKCS5Paddingpublic final static String AES_DEFAULT = "AES";public final static String AES_CBC_NoPadding = "AES/CBC/NoPadding";public final static String AES_CBC_PKCS5Padding = "AES/CBC/PKCS5Padding";public final static String AES_CBC_ISO10126Padding = "AES/CBC/ISO10126Padding";public final static String AES_CFB_NoPadding = "AES/CFB/NoPadding";public final static String AES_CFB_PKCS5Padding = "AES/CFB/PKCS5Padding";public final static String AES_CFB_ISO10126Padding = "AES/CFB/ISO10126Padding";public final static String AES_ECB_NoPadding = "AES/ECB/NoPadding";public final static String AES_ECB_PKCS5Padding = "AES/ECB/PKCS5Padding";public final static String AES_ECB_ISO10126Padding = "AES/ECB/ISO10126Padding";public final static String AES_OFB_NoPadding = "AES/OFB/NoPadding";public final static String AES_OFB_PKCS5Padding = "AES/OFB/PKCS5Padding";public final static String AES_OFB_ISO10126Padding = "AES/OFB/ISO10126Padding";public final static String AES_PCBC_NoPadding = "AES/PCBC/NoPadding";public final static String AES_PCBC_PKCS5Padding = "AES/PCBC/PKCS5Padding";public final static String AES_PCBC_ISO10126Padding = "AES/PCBC/ISO10126Padding";
}
另附代码
package net.trueland.employee.common;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;import com.alibaba.fastjson.JSONObject;public class SecurityUtils {public static final String ALGORITHM_AES = "AES";public static final String CHARSET_UTF8 = "UTF-8";/*** 参数加签* @param json 参数,仅支持json对象* @param key 密钥* @return 密文*/public static String signParam(String json, String key) throws Exception {if (StringUtils.isBlank(json)) {return StringUtils.EMPTY;}if (StringUtils.isBlank(key)) {throw new Exception("param add sign error: no key");}StringBuffer stringBuffer = new StringBuffer(key);JSONObject jsonObject = JSONObject.parseObject(json);List<String> keyList = new ArrayList<String>(jsonObject.keySet());keyList.remove("sign");Collections.sort(keyList);for (String s : keyList) {Object o = jsonObject.get(s);if (o == null) {o = "";}stringBuffer.append(s);stringBuffer.append(o);}return stringBuffer.toString();}/*** md5 加密* @param str 明文* @return*/public static String encryptMD5(String str) {MessageDigest md = null;try {md = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {// TODO 异常处理}md.update(str.getBytes());byte[] b = md.digest();StringBuilder buf = new StringBuilder();byte[] var5 = b;int var6 = b.length;for (int var7 = 0; var7 < var6; ++var7) {byte aB = var5[var7];int i = aB;if (aB < 0) {i = aB + 256;}if (i < 16) {buf.append("0");}buf.append(Integer.toHexString(i));}return buf.toString();}// ------------------------------------------AES/ECB/PKCS5Padding加解密, AES默认的加密方式--------------------------------------------------/*** 加密速度快* 该模式不可以使用向量,否则报错:java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV* @param plaintext 明文* @param key 密钥 不强制位数* @return 密文*/public static String encrypt(String plaintext, String key) throws Exception {return encryptOrDecrypt(Cipher.ENCRYPT_MODE, plaintext, key, null, "AES/ECB/PKCS5Padding");}/*** 加密速度快* 该模式不可以使用向量,否则报错:java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV* @param ciphertext 密文* @param key 密钥 不强制位数* @return 明文*/public static String decrypt(String ciphertext, String key) throws Exception {return encryptOrDecrypt(Cipher.DECRYPT_MODE, ciphertext, key, null, "AES/ECB/PKCS5Padding");}// ------------------------------------------AES/CBC/PKCS5Padding 加解密--------------------------------------------------/*** 安全性高,加密速度慢* CBC模式必须提供向量,否则报错:java.security.InvalidKeyException: Parameters missing* @param plaintext 明文* @param key 密钥,不强制位数* @param offset 偏移量,提高安全性,强制16位* @return 密文*/public static String encrypt(String plaintext, String key, String offset) throws Exception {return encryptOrDecrypt(Cipher.ENCRYPT_MODE, plaintext, key, offset.getBytes(CHARSET_UTF8), "AES/CBC/PKCS5Padding");}/*** 安全性高,加密速度慢* CBC模式必须提供向量,否则报错:java.security.InvalidKeyException: Parameters missing* @param ciphertext 密文* @param key 密钥,不强制位数* @param offset 偏移量,提高安全性,强制16位* @return 明文*/public static String decrypt(String ciphertext, String key, String offset) throws Exception {return encryptOrDecrypt(Cipher.DECRYPT_MODE, ciphertext, key, offset.getBytes(CHARSET_UTF8), "AES/CBC/PKCS5Padding");}/*** 加密或解密* @param mode 加密1 解密2* @param content 明文或密文* @param key 密钥* @param iv 向量* @param modeAndPadding 加密类型和填充类型* @return* @throws Exception*/private static String encryptOrDecrypt(int mode, String content, String key, byte[] iv, String modeAndPadding) throws Exception {byte[] byteContent = Cipher.ENCRYPT_MODE == mode ? content.getBytes(CHARSET_UTF8) : Base64.decodeBase64(content);// 密钥生成器KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);// 随机数,不可以随时间随机,否则mac,linux报错SecureRandom random = SecureRandom.getInstance("SHA1PRNG");// 设置随机种子random.setSeed(key.getBytes(CHARSET_UTF8));// 初始化密钥生成器keyGenerator.init(128, random);// 生成密钥SecretKey secretKey = keyGenerator.generateKey();// 生成AES专用密钥SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);// 创建加/解密器Cipher cipher = Cipher.getInstance(modeAndPadding);// 初始化加/解密器if (null != iv){// 指定向量,提高安全性cipher.init(mode,secretKeySpec,new IvParameterSpec(iv));}else {cipher.init(mode, secretKeySpec);}// 加密/解密byte[] result = cipher.doFinal(byteContent);return Cipher.ENCRYPT_MODE == mode ? Base64.encodeBase64String(result) : new String(result);}/*** 随机生成指定长度的密钥* @param length*/public static String randomKey(int length){return RandomStringUtils.randomAlphanumeric(length);}
}
RSA非对称加密
public class RSAEncrypt {private static Map<Integer, String> keyMap = new HashMap<Integer, String>(); //用于封装随机产生的公钥与私钥public static void main(String[] args) throws Exception {//生成公钥和私钥genKeyPair();//加密字符串String message = "df723820";System.out.println("随机生成的公钥为:" + keyMap.get(0));System.out.println("随机生成的私钥为:" + keyMap.get(1));String messageEn = encrypt(message,keyMap.get(0));System.out.println(message + "\t加密后的字符串为:" + messageEn);String messageDe = decrypt(messageEn,keyMap.get(1));System.out.println("还原后的字符串为:" + messageDe);}/** * 随机生成密钥对 * @throws NoSuchAlgorithmException */ public static void genKeyPair() throws NoSuchAlgorithmException { // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // 初始化密钥对生成器,密钥大小为96-1024位 keyPairGen.initialize(1024,new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥 String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); // 得到私钥字符串 String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); // 将公钥和私钥保存到MapkeyMap.put(0,publicKeyString); //0表示公钥keyMap.put(1,privateKeyString); //1表示私钥} /** * RSA公钥加密 * * @param str * 加密字符串* @param publicKey * 公钥 * @return 密文 * @throws Exception * 加密过程中的异常信息 */ public static String encrypt( String str, String publicKey ) throws Exception{//base64编码的公钥byte[] decoded = Base64.decodeBase64(publicKey);RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));//RSA加密Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE, pubKey);String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));return outStr;}/** * RSA私钥解密* * @param str * 加密字符串* @param privateKey * 私钥 * @return 铭文* @throws Exception * 解密过程中的异常信息 */ public static String decrypt(String str, String privateKey) throws Exception{//64位解码加密后的字符串byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));//base64编码的私钥byte[] decoded = Base64.decodeBase64(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); //RSA解密Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, priKey);String outStr = new String(cipher.doFinal(inputByte));return outStr;}}