每日一例 | java常用的加密算法实现
前言
日常开发中,传输敏感数据,或者登陆、权限校验的时候,我们经常要对数据进行加密解密,今天我们就来看下如何用java
来实现这些常用的加密算法。
常用的加密算法分为两种,“对称式”和“非对称式”。
对称式加密技术
对称式加密就是加密和解密使用同一个密钥,通常称之为“Session Key
”这种加密技术在当今被广泛采用,如美国政府所采用的DES
加密标准就是一种典型的“对称式”加密法,它的Session Key
长度为56bits
。
非对称式加密技术
非对称式加密就是加密和解密所使用的不是同一个密钥,通常有两个密钥,称为“公钥”和“私钥”,它们两个必需配对使用,否则不能打开加密文件。这里的“公钥”是指可以对外公布的,“私钥”则不能,只能由持有人一个人知道。它的优越性就在这里,因为对称式的加密方法如果是在网络上传输加密文件就很难不把密钥告诉对方,不管用什么方法都有可能被别窃听到。而非对称式的加密方法有两个密钥,且其中的“公钥”是可以公开的,也就不怕别人知道,收件人解密时只要用自己的私钥即可以,这样就很好地避免了密钥的传输安全性问题。
我们国家出于信息安全考虑,研发了国密算法。国密算法,就是国家密码局认定的国产密码算法,即商用密码。
国密算法是国家密码局制定标准的一系列算法。其中包括了对称加密算法,椭圆曲线非对称加密算法,杂凑算法。具体包括SM1,SM2,SM3等。
有兴趣的小伙伴可以去国家密码管理局(https://www.oscca.gov.cn/sca/index.shtml
)了解下,网上也有很多现成的资源,今天我们就不讲了。
常用的算法
Base64
严格意义上讲,base64
只能算一种编码格式,并不能算加密,因为任何人拿到base64
都可以解码,但是base64
在日常开发中经常被用到,比如参数编码之后传输、图片编码后传输
/**
* base64加密
*
* @param sourcesStr
* @return
*/
public String encodeBase64(String sourcesStr) {
Base64.Encoder encoder = Base64.getEncoder();
String encodeToString = encoder.encodeToString(sourcesStr.getBytes());
return encodeToString;
}
/**
* base64解码
*
* @param encodeBase64Str
* @return
*/
public String decodeBase64(String encodeBase64Str) {
Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(encodeBase64Str);
return new String(decode);
}
MD5
md5
是一种不可被解密的加密方式,网上提供解密的网站,基本上都是先生成密码库(密文和明文的kv
),当你输入密文后,检索对应的明文,如果存在就可以被解密,否则是没法解密的,这种方式就是暴力破解,当然如果某一天进入了量子时代,那秒破md5
也是可以的,但以目前的计算机算力,暴力破解一个超长的md5
密文,那是相当难的
/**
* md5加密
* @param sourcesStr
* @return
*/
public String md5Encode(String sourcesStr) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
sourcesStr.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有这个md5算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
RSA
非对称加密算法的代表,RSA
是被研究得最广泛的公钥算法,从提出到现在已近三十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。1983
年麻省理工学院在美国为RSA
算法申请了专利。
RSA
允许你选择公钥的大小。512
位的密钥被视为不安全的;768
位的密钥不用担心受到除了国家安全管理(NSA
)外的其他事物的危害;1024
位的密钥几乎是安全的。RSA
在一些主要产品内部都有嵌入,像 Windows
、网景 Navigator
、 Quicken
和 Lotus Notes
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Hex;
public class RSATest {
// 数字签名,密钥算法
private static final String RSA_KEY_ALGORITHM = "RSA";
// 数字签名签名/验证算法
private static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
// RSA密钥长度
private static final int KEY_SIZE = 1024;
public static final String PUBLIC_KEY = "publicKey";
public static final String PRIVATE_KEY = "privateKey";
/**
* 初始化RSA密钥对
* @return RSA密钥对
* @throws Exception 抛出异常
*/
public static Map<String, String> initKey() throws Exception {
KeyPairGenerator keygen = KeyPairGenerator
.getInstance(RSA_KEY_ALGORITHM);
SecureRandom secrand = new SecureRandom();
secrand.setSeed("hahaha".getBytes());// 初始化随机产生器
keygen.initialize(KEY_SIZE, secrand); // 初始化密钥生成器
KeyPair keys = keygen.genKeyPair();
String pub_key = new String(Hex.encodeHex(keys.getPublic().getEncoded()));
String pri_key = new String(Hex.encodeHex(keys.getPrivate().getEncoded()));
Map<String, String> keyMap = new HashMap<String, String>();
keyMap.put(PUBLIC_KEY, pub_key);
keyMap.put(PRIVATE_KEY, pri_key);
System.out.println("公钥:" + pub_key);
System.out.println("私钥:" + pri_key);
return keyMap;
}
/**
* 得到公钥
* @param keyMap RSA密钥对
* @return 公钥
* @throws Exception 抛出异常
*/
public static String getPublicKey(Map<String, String> keyMap) throws Exception{
return keyMap.get(PUBLIC_KEY);
}
/**
* 得到私钥
* @param keyMap RSA密钥对
* @return 私钥
* @throws Exception 抛出异常
*/
public static String getPrivateKey(Map<String, String> keyMap) throws Exception{
return keyMap.get(PRIVATE_KEY);
}
/**
* 数字签名
* @param data 待签名数据
* @param pri_key 私钥
* @return 签名
* @throws Exception 抛出异常
*/
public static String sign(byte[] data, String pri_key) throws Exception {
// 取得私钥
byte[] pri_key_bytes = Hex.decodeHex(pri_key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pri_key_bytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
// 生成私钥
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
return new String(Hex.encodeHex(signature.sign()));
}
/**
* RSA校验数字签名
* @param data 数据
* @param sign 签名
* @param pub_key 公钥
* @return 校验结果,成功为true,失败为false
* @throws Exception 抛出异常
*/
public static boolean verify(byte[] data, byte[] sign, String pub_key) throws Exception {
// 转换公钥材料
// 实例化密钥工厂
byte[] pub_key_bytes = Hex.decodeHex(pub_key.toCharArray());
KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
// 初始化公钥
// 密钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pub_key_bytes);
// 产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
/**
* 公钥加密
* @param data 待加密数据
* @param pub_key 公钥
* @return 密文
* @throws Exception 抛出异常
*/
private static byte[] encryptByPubKey(byte[] data, byte[] pub_key) throws Exception {
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pub_key);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
* @param data 待加密数据
* @param pub_key 公钥
* @return 密文
* @throws Exception 抛出异常
*/
public static String encryptByPubKey(String data, String pub_key) throws Exception {
// 私匙加密
byte[] pub_key_bytes = Hex.decodeHex(pub_key.toCharArray());
byte[] enSign = encryptByPubKey(data.getBytes(), pub_key_bytes);
return new String(Hex.encodeHex(enSign));
}
/**
* 私钥加密
* @param data 待加密数据
* @param pri_key 私钥
* @return 密文
* @throws Exception 抛出异常
*/
private static byte[] encryptByPriKey(byte[] data, byte[] pri_key) throws Exception {
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pri_key);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
* @param data 待加密数据
* @param pri_key 私钥
* @return 密文
* @throws Exception 抛出异常
*/
public static String encryptByPriKey(String data, String pri_key) throws Exception {
// 私匙加密
byte[] pri_key_bytes = Hex.decodeHex(pri_key.toCharArray());
byte[] enSign = encryptByPriKey(data.getBytes(), pri_key_bytes);
return new String(Hex.encodeHex(enSign));
}
/**
* 公钥解密
* @param data 待解密数据
* @param pub_key 公钥
* @return 明文
* @throws Exception 抛出异常
*/
private static byte[] decryptByPubKey(byte[] data, byte[] pub_key) throws Exception {
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pub_key);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
* @param data 待解密数据
* @param pub_key 公钥
* @return 明文
* @throws Exception 抛出异常
*/
public static String decryptByPubKey(String data, String pub_key) throws Exception {
// 公匙解密
byte[] pub_key_bytes = Hex.decodeHex(pub_key.toCharArray());
byte[] design = decryptByPubKey(Hex.decodeHex(data.toCharArray()), pub_key_bytes);
return new String(design);
}
/**
* 私钥解密
* @param data 待解密数据
* @param pri_key 私钥
* @return 明文
* @throws Exception 抛出异常
*/
private static byte[] decryptByPriKey(byte[] data, byte[] pri_key) throws Exception {
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pri_key);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
* @param data 待解密数据
* @param pri_key 私钥
* @return 明文
* @throws Exception 抛出异常
*/
public static String decryptByPriKey(String data, String pri_key) throws Exception {
// 私匙解密
byte[] pri_key_bytes = Hex.decodeHex(pri_key.toCharArray());
byte[] design = decryptByPriKey(Hex.decodeHex(data.toCharArray()), pri_key_bytes);
return new String(design);
}
/**
* @param args
*/
@SuppressWarnings("static-access")
public static void main(String[] args) throws Exception {
RSATest das = new RSATest();
String datastr = "天街小雨润如酥,草色遥看近却无。最是一年春好处,绝胜烟柳满皇都。";
System.out.println("待加密数据:\n" + datastr);
//获取密钥对
Map<String, String> keyMap = new HashMap<String, String>();
keyMap = das.initKey();
String pub_key = (String) keyMap.get(PUBLIC_KEY);
String pri_key = (String) keyMap.get(PRIVATE_KEY);
// 公匙加密
String pubKeyStr = RSATest.encryptByPubKey(datastr, pub_key);
System.out.println("公匙加密结果:\n" + pubKeyStr);
// 私匙解密
String priKeyStr = RSATest.decryptByPriKey(pubKeyStr, pri_key);
System.out.println("私匙解密结果:\n" + priKeyStr);
//换行
System.out.println();
// 数字签名
String str1 = "123";
String str2 = "123er";
System.out.println("正确的签名:" + str1 + "\n错误的签名:" + str2);
String sign = RSATest.sign(str1.getBytes(), pri_key);
System.out.println("数字签名:\n" + sign);
System.out.println("签名长度:" + sign.length());
boolean vflag1 = das.verify(str1.getBytes(), Hex.decodeHex(sign.toCharArray()), pub_key);
System.out.println("数字签名验证结果1:\n" + vflag1);
boolean vflag2 = das.verify(str2.getBytes(), Hex.decodeHex(sign.toCharArray()), pub_key);
System.out.println("数字签名验证结果2:\n" + vflag2);
}
}
另外两种rsa
加密算法——MD5withRSA
和SHA1withRSA
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import client.util.HEX2Byte;
public class DigitalSignatureMain {
public static void main(String[] args) throws Exception {
String content = "study hard and make progress everyday";
System.out.println("content :" + content);
KeyPair keyPair = getKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String md5Sign = getMd5Sign(content, privateKey);
System.out.println("sign with md5 and rsa :" + md5Sign);
boolean md5Verifty = verifyWhenMd5Sign(content, md5Sign, publicKey);
System.out.println("verify sign with md5 and rsa :" + md5Verifty);
String sha1Sign = getSha1Sign(content, privateKey);
System.out.println("签名长度:" + sha1Sign.length());
System.out.println("sign with sha1 and rsa :" + sha1Sign);
boolean sha1Verifty = verifyWhenSha1Sign(content, sha1Sign, publicKey);
System.out.println("verify sign with sha1 and rsa :" + sha1Verifty);
}
// 生成密钥对
private static KeyPair getKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(512); // 可以理解为:加密后的密文长度,实际原文要小些 越大 加密解密越慢
KeyPair keyPair = keyGen.generateKeyPair();
return keyPair;
}
// 用md5生成内容摘要,再用RSA的私钥加密,进而生成数字签名
public static String getMd5Sign(String content, PrivateKey privateKey)
throws Exception {
byte[] contentBytes = content.getBytes("utf-8");
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
signature.update(contentBytes);
byte[] signs = signature.sign();
return HEX2Byte.bytesToHex(signs);
}
// 对用md5和RSA私钥生成的数字签名进行验证
public static boolean verifyWhenMd5Sign(String content, String sign,
PublicKey publicKey) throws Exception {
byte[] contentBytes = content.getBytes("utf-8");
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
signature.update(contentBytes);
return signature.verify(HEX2Byte.hexToByteArray(sign));
}
// 用sha1生成内容摘要,再用RSA的私钥加密,进而生成数字签名
public static String getSha1Sign(String content, PrivateKey privateKey)
throws Exception {
byte[] contentBytes = content.getBytes("utf-8");
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(privateKey);
signature.update(contentBytes);
byte[] signs = signature.sign();
return HEX2Byte.bytesToHex(signs);
}
// 对用md5和RSA私钥生成的数字签名进行验证
public static boolean verifyWhenSha1Sign(String content, String sign,
PublicKey publicKey) throws Exception {
byte[] contentBytes = content.getBytes("utf-8");
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
signature.update(contentBytes);
return signature.verify(HEX2Byte.hexToByteArray(sign));
}
}
工具类
package client.util;
public class HEX2Byte {
/**
* hex字符串转byte数组
*
* @param inHex
* 待转换的Hex字符串
* @return 转换后的byte数组结果
*/
public static byte[] hexToByteArray(String inHex) {
int hexlen = inHex.length();
byte[] result;
if (hexlen % 2 == 1) {
// 奇数
hexlen++;
result = new byte[(hexlen / 2)];
inHex = "0" + inHex;
} else {
// 偶数
result = new byte[(hexlen / 2)];
}
int j = 0;
for (int i = 0; i < hexlen; i += 2) {
result[j] = hexToByte(inHex.substring(i, i + 2));
j++;
}
return result;
}
/**
* Hex字符串转byte
*
* @param inHex
* 待转换的Hex字符串
* @return 转换后的byte
*/
public static byte hexToByte(String inHex) {
return (byte) Integer.parseInt(inHex, 16);
}
/**
* 字节数组转16进制
*
* @param bytes
* 需要转换的byte数组
* @return 转换后的Hex字符串
*/
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
}
总结
加密算法这块其实没什么好总结的,毕竟算法大佬已经帮你实现好了,你要做的就是把它改造成你需要的样子。个人觉得,现在做软件开发,让完全胜任自己的工作,有一个能力特别重要,也特别基础,就是改造轮子的能力,毕竟现在开发程序不可能任何需求都从零开始,那太影响效率了,而且你造的轮子性能也不见得好。这也就是说,你要有看透轮子本质的火眼金睛,要知道轮子解决了什么问题,是否满足你的需求,但这还是要回到造轮子上,只是功夫要花在平时学习的时候,这是一个需要长期投入的事,当然对你的成长也是特别有助益的。好了,今天就到这里吧!
项目路径:
https://github.com/Syske/example-everyday
本项目会每日更新,让我们一起学习,一起进步,遇见更好的自己,加油呀
- END -