Java常用加密方式
一,加密算法分类
对称加密:指加密和解密的密钥相同,优点就是加解密的效率高且易于实现。
非对称加密:指加密和解密的密钥不相同,也称为公私要加密。
不可逆加密:特征就是加密过程不需要密钥,并且加密后的数据不能被解密,只能输入同样的数据并且经过同样的不可逆加密算法才能获取同样的加密数据。
二,加密算法的应用
1.数字签名:进行身份认证和数据完整性验证,主要用到了非对称密钥加密技术与数字摘要技术。
2.数字证书:主要用来确保数字签名是安全有效的,数字证书由独立的证书发行机构发布。数字证书各不相同,每种证书可提供不同级别的可信度,该证书内包含用户的个人信息和他的公钥信息,同时还附有认证中心的签名信息。
3.MD5:对用户密码进行加密并进行保存。
4.网络数据加密:保障传输的数据安全,即使被截获报文,在没有密匙的情况下也无法得知报文真实内容。
5.SSL协议:在握手阶段使用的是非对称加密,在传输阶段使用的是对称加密,也就是说在SSL上传送的数据是使用对称密钥加密的。同时HTTPS也是由SSL+HTTP协议构建的可进行加密传输、身份认证(确认客户端连接的目标主机是否是真实正确的主机)的网络协议。
三,对称加密算法
-
优点:算法对消息双方公开、计算量小、加解密速度快、效率高。
-
缺点:在数据传送前,发送方和接收方必须商定好秘钥,然后双方保存好秘钥。如果一方的秘钥被泄露,那么加密信息就会被破解。
3.1 DES介绍
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,24小时内即可被破解。
DES加密和解密过程中,密钥长度都必须是8的倍数。
Java代码实现:
import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import java.nio.charset.StandardCharsets; import java.security.SecureRandom;/*** @ClassName: DESHelper* @Description:* @Author: Fufeng* @Date: 2022/7/31 20:45*/ public class DESHelper {public String encrypt(String dataSource, String password) throws Exception {//DES算法要求有一个可信任的随机数源SecureRandom random = new SecureRandom();DESKeySpec desKey = new DESKeySpec(password.getBytes(StandardCharsets.UTF_8));//创建一个密钥工厂,然后用它对desKey进行转换SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");SecretKey secretKey = keyFactory.generateSecret(desKey);//Cipher对象实际完成加密操作Cipher cipher = Cipher.getInstance("DES");//用密钥初始化Cipher对象,ENCRYPT_MODE用于将Cipher初始化为加密模式的常量cipher.init(Cipher.ENCRYPT_MODE,secretKey,random);//正式对数据进行加密操作return new String(cipher.doFinal(dataSource.getBytes(StandardCharsets.UTF_8)));}public String decrypt(String src, String password) throws Exception {SecureRandom random = new SecureRandom();DESKeySpec desKey = new DESKeySpec(password.getBytes(StandardCharsets.UTF_8));SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");SecretKey secretKey = keyFactory.generateSecret(desKey);//Cipher对象实际完成解密操作Cipher cipher = Cipher.getInstance("DES");cipher.init(Cipher.DECRYPT_MODE,secretKey,random);return new String(cipher.doFinal(src.getBytes(StandardCharsets.UTF_8)));} }
加解密测试:
@Testpublic void DESTest() throws Exception {DESHelper desHelper = new DESHelper();String source = "满天星辰不及你!";System.out.println("原始数据:"+source);byte[] encryptData = desHelper.encrypt(source, "1qaz2wsx");System.out.println("加密后数据:"+encryptData);byte[] decryptData = desHelper.decrypt(encryptData, "1qaz2wsx");System.out.println("解密后数据:"+new String(decryptData));}
3.2 IDEA介绍
- 这种算法是在DES算法的基础上发展出来的,类似于三重DES。
- 发展IDEA也是因为感到DES具有密钥太短等缺点。
- DEA的密钥为128位,这么长的密钥在今后若干年内应该是安全的。
Java代码实现:
import org.bouncycastle.jce.provider.BouncyCastleProvider;import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.security.Security;/*** @ClassName: IDEA* @Description:* @Author: Fufeng* @Date: 2022/7/31 21:07*/ public class IDEAHelper {/*** 密钥算法* */public static final String KEY_ALGORITHM="IDEA";/*** 加密/解密算法/工作模式/填充方式* */public static final String CIPHER_ALGORITHM="IDEA/ECB/ISO10126Padding";/**** 生成密钥,只有bouncycastle支持* @return byte[] 二进制密钥* */public static byte[] initKey() throws Exception{//加入bouncyCastle支持Security.addProvider(new BouncyCastleProvider());//实例化密钥生成器KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM);//初始化密钥生成器,IDEA要求密钥长度为128位kg.init(128);//生成密钥SecretKey secretKey=kg.generateKey();//获取二进制密钥编码形式return secretKey.getEncoded();}/*** 转换密钥* @param key 二进制密钥* @return Key 密钥* */public static Key toKey(byte[] key) throws Exception{//实例化DES密钥//生成密钥SecretKey secretKey=new SecretKeySpec(key,KEY_ALGORITHM);return secretKey;}/*** 加密数据* @param data 待加密数据* @param key 密钥* @return byte[] 加密后的数据* */public static byte[] encrypt(byte[] data,byte[] key) throws Exception{//加入bouncyCastle支持Security.addProvider(new BouncyCastleProvider());//还原密钥Key k=toKey(key);//实例化Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);//初始化,设置为加密模式cipher.init(Cipher.ENCRYPT_MODE, k);//执行操作return cipher.doFinal(data);}/*** 解密数据* @param data 待解密数据* @param key 密钥* @return byte[] 解密后的数据* */public static byte[] decrypt(byte[] data,byte[] key) throws Exception{//加入bouncyCastle支持Security.addProvider(new BouncyCastleProvider());//还原密钥Key k =toKey(key);Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);//初始化,设置为解密模式cipher.init(Cipher.DECRYPT_MODE, k);//执行操作return cipher.doFinal(data);} }
加解密测试:
@Test public void IDEATest() throws Exception {String str="满天星辰不及你!";System.out.println("原始数据:"+str);//初始化密钥byte[] key=IDEAHelper.initKey();System.out.println("密钥:"+ Base64.encodeBase64String(key));//加密数据byte[] data=IDEAHelper.encrypt(str.getBytes(), key);System.out.println("加密后数据:"+Base64.encodeBase64String(data));data=IDEAHelper.decrypt(data, key);System.out.println("解密后数据:"+new String(data));}
四,非对称加密算法
- 优点:非对称加密与对称加密相比其安全性更好,只要私钥不泄露,很难被破解。
- 缺点:加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
4.1 RSA介绍
RSA是目前最有影响力和最常用的公钥加密算法。它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。RSA公开密钥密码体制的原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
Java代码实现:
import org.apache.commons.codec.binary.Base64;import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;/*** @ClassName: RSAHelper* @Description:* @Author: Fufeng* @Date: 2022/8/1 20:57*/
public class RSAHelper {public static final String KEY_ALGORITHM = "RSA";public static final String SIGNATURE_ALGORITHM = "MD5withRSA";private static final String PUBLIC_KEY = "RSAPublicKey";private static final String PRIVATE_KEY = "RSAPrivateKey";public static byte[] decryptBASE64(String key) {return Base64.decodeBase64(key);}public static String encryptBASE64(byte[] bytes) {return Base64.encodeBase64String(bytes);}/*** 用私钥对信息生成数字签名** @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));}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);}/*** 用私钥解密** @param data* @param key* @return* @throws Exception*/public static byte[] decryptByPrivateKey(String data, String key) throws Exception {return decryptByPrivateKey(decryptBASE64(data), key);}/*** 用公钥解密** @param data* @param key* @return* @throws Exception*/public static byte[] decryptByPublicKey(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.DECRYPT_MODE, publicKey);return cipher.doFinal(data);}/*** 用公钥加密** @param data* @param key* @return* @throws Exception*/public static byte[] encryptByPublicKey(String 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.getBytes());}/*** 用私钥加密** @param data* @param key* @return* @throws Exception*/public static byte[] encryptByPrivateKey(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.ENCRYPT_MODE, privateKey);return cipher.doFinal(data);}/*** 取得私钥** @param keyMap* @return* @throws Exception*/public static String getPrivateKey(Map<String, Key> keyMap) throws Exception {Key key = (Key) keyMap.get(PRIVATE_KEY);return encryptBASE64(key.getEncoded());}/*** 取得公钥** @param keyMap* @return* @throws Exception*/public static String getPublicKey(Map<String, Key> keyMap) throws Exception {Key key = keyMap.get(PUBLIC_KEY);return encryptBASE64(key.getEncoded());}/*** 初始化密钥** @return* @throws Exception*/public static Map<String, Key> initKey() throws Exception {KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGen.initialize(1024);KeyPair keyPair = keyPairGen.generateKeyPair();Map<String, Key> keyMap = new HashMap(2);keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥return keyMap;}}
加解密测试:
@Test
public void RSATest() throws Exception {Map<String, Key> keyMap = initKey();String publicKey = getPublicKey(keyMap);String privateKey = getPrivateKey(keyMap);System.out.println("公钥:"+publicKey);System.out.println("-----------------------------------");System.out.println("私钥:"+privateKey);System.out.println("-----------------------------------");String src = "满天星辰不及你!";System.out.println("原始数据:"+src);System.out.println("-----------------------------------");byte[] encryptByPrivateKey = encryptByPrivateKey(src.getBytes(), privateKey);byte[] encryptByPublicKey = encryptByPublicKey(src, publicKey);System.out.println("私钥加密后数据"+encryptByPrivateKey);System.out.println("-----------------------------------");System.out.println("公钥加密后数据"+encryptByPublicKey);System.out.println("-----------------------------------");String sign = sign(encryptByPrivateKey, privateKey);System.out.println("数字签名:"+sign);System.out.println("-----------------------------------");boolean verify = verify(encryptByPrivateKey, publicKey, sign);System.out.println("签名验证结果:"+verify);System.out.println("-----------------------------------");byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey);byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey);System.out.println("公钥解密私钥加密后的数据:"+new String(decryptByPublicKey));System.out.println("-----------------------------------");System.out.println("私钥解密公钥加密后的数据:"+new String(decryptByPrivateKey));
}
五,不可逆算法
5.1 MD5介绍
MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式
(也就是把一个任意长度的字节串变换成一定长的十六进制数字串)。
主要有以下特点:
- 1.压缩性: 任意长度的数据,算出的MD5值长度都是固定的。
- 2.容易计算: 从原数据计算出MD5值很容易。
- 3.抗修改性: 对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 4.强抗碰撞: 已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
Java代码实现:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** @ClassName: MD5Util* @Description:* @Author: Fufeng* @Date: 2022/7/31 17:14*/
public class MD5Helper {public String encode(String s) throws NoSuchAlgorithmException {MessageDigest md5 = MessageDigest.getInstance("MD5");byte[] md5Bytes = md5.digest(s.getBytes(StandardCharsets.UTF_8));return md5ToString(md5Bytes);}//将md5数组转化为16进制字符串public String md5ToString(byte[] md5Bytes) {StringBuilder hexValue = new StringBuilder();for (int i = 0; i < md5Bytes.length; i++) {int val = md5Bytes[i] & 0Xff;if (val < 16){hexValue.append("0");}hexValue.append(Integer.toHexString(val));}return hexValue.toString();}}
加密测试:
@Test
public void MD5Test() throws NoSuchAlgorithmException {MD5Helper md5Helper = new MD5Helper();String src1 = "满天星辰不及你!";String src2 = "满天星辰不及你!";System.out.println("src1 加密后数据:"+md5Helper.encode(src1));System.out.println("src2 加密后数据:"+md5Helper.encode(src2));
}
5.2 SHA1介绍
对于长度小于2^64位的消息,SHA1会产生一个160位(40个字符)的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。
SHA1有如下特性:
- 不可以从消息摘要中复原信息;
- 两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机率出现相同的消息摘要,一般使用时忽略)。
Java代码实现:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** @ClassName: SHA1Util* @Description:* @Author: Fufeng* @Date: 2022/7/31 20:16*/
public class SHA1Helper {public String encode(String str) throws NoSuchAlgorithmException {if (null == str || str.length() == 0){return null;}char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };//创建SHA1算法消息摘要对象MessageDigest mdTemp = MessageDigest.getInstance("SHA1");//使用指定的字节数组更新摘要mdTemp.update(str.getBytes(StandardCharsets.UTF_8));//生成hash值的字节数组byte[] md = mdTemp.digest();//SHA1算法生成信息摘要的关键过程int j = md.length;char[] buf = new char[j*2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];buf[k++] = hexDigits[byte0 >>> 4 & 0xf];buf[k++] = hexDigits[byte0 & 0xf];}return new String(buf);}
}
加密测试:
@Test
public void SHA1Test() throws NoSuchAlgorithmException {SHA1Helper sha1Helper = new SHA1Helper();String src1 = "满天星辰不及你!";String src2 = "满天星辰不及你!";System.out.println("src1 加密后数据:"+sha1Helper.encode(src1));System.out.println("src2 加密后数据:"+sha1Helper.encode(src2));
}
5.3 HMAC 介绍
HMAC 是密钥相关的 哈希运算消息认证码(Hash-based Message Authentication Code),HMAC 运算利用 哈希算法 (MD5、SHA1 等),以 一个密钥 和 一个消息 为输入,生成一个 消息摘要 作为 输出。
HMAC 发送方 和 接收方 都有的 key 进行计算,而没有这把 key 的第三方,则是 无法计算 出正确的 散列值的,这样就可以 防止数据被篡改。
Java代码实现:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;/*** @ClassName: HMACHelper* @Description:* @Author: Fufeng* @Date: 2022/7/31 20:31*/
public class HMACHelper {private Mac mac;/*** MAC算法可选以下多种算法* HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512*/private static final String KEY_MAC = "HmacMD5";public HMACHelper(String key) throws Exception {SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), KEY_MAC);mac = Mac.getInstance(secretKey.getAlgorithm());mac.init(secretKey);}public String sign(String content){return new String(mac.doFinal(content.getBytes(StandardCharsets.UTF_8)));}public boolean verify(String signature, String content){byte[] result = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));return Arrays.equals(result,signature.getBytes(StandardCharsets.UTF_8));}
}
加密测试:
@Test
public void HMACTest() throws Exception {HMACHelper hmacHelper = new HMACHelper("123456");String src = "满天星辰不及你!";byte[] signature = hmacHelper.sign(src);boolean b = hmacHelper.verify(signature, src);System.out.println("src 生成数字签名:"+signature);System.out.println("签名验证结果:"+b);
}