DigestUtils和MessageDigest是Java中用于进行消息摘要的两个类。它们之间的区别如下:
功能:DigestUtils是Apache Commons Codec库中的一个工具类,提供了对消息摘要算法的封装,包括MD5、SHA-1、SHA-256等算法。它提供了简单易用的静态方法来计算消息摘要。MessageDigest是Java标准库中的类,提供了实现特定消息摘要算法的功能。
调用方式:DigestUtils提供了静态方法,可以直接在代码中调用,无需创建实例。而MessageDigest需要通过getInstance方法获取实例,并使用update方法来更新要计算的数据,最后调用digest方法来计算并返回摘要值。
依赖:DigestUtils需要依赖Apache Commons Codec库,需要在项目中添加对应的依赖。而MessageDigest是Java标准库的一部分,无需额外添加依赖。
数字签名
主要用于保证数据的完整性、真实性和身份认证,通过利用私钥对原始数据进行签名,然后将签名附加到原始数据上,接收方再利用公钥对签名验证数据的真实性和来源合法性。在数字签名中广泛使用的算法包括:RSA、DSA、ECDSA 等。
数据加密
主要用于保护数据的机密性,通过使用公钥和私钥等加密算法对数据信息进行加密和解密,只有拥有正确秘钥的个人或组织才能够进行解密操作。一般情况下,数据加密会涉及到两种类型的加密算法,分别为对称加密算法和非对称加密算法。在对称加密算法中,常见的算法包括:AES、DES、3DES 等;在非对称加密算法中,常见的算法包括 RSA、ECC、Diffie-Hellman(DH)等。数字签名和数据加密在实际应用中常常需要结合使用
数字签名与数据加密的区别
1.实现过程不同
数字签名和数字加密的过程都使用公开密钥体系,但实现的过程正好相反,数字签名
使用的是发送方的密钥,发送方
用自己的私有密钥进行加密
,接收方用发送方的公开密钥进行解密,是一对多
的关系,任何拥有发送方公开密钥的人都可以验证数字签名的正确性;而数字加密
则使用的是接收方的密钥,是多对一
的关系,任何知道接收方公开密钥的人都可以向接收方发送加密信息,只有唯一拥有接收方私有密钥
的人才能对信息解密。
2.加密算法不同
数字签名只采用了非对称密钥加密
算法,它能保证发送信息的完整性(校验信息有没有被修改过)、身份认证和不可否认性;而数字加密是采用了对称密钥加密算法和非对称密钥加密算法
相结合的方法,它能保证发送信息保密性(杜绝防止别人看到信息内容)。
BASE64编码格式
BASE64 严格地说,属于编码格式,而非加密算法。BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。常见于邮件、http加密,截取http信息。
//加密字符串
public static String decodeData(String str) {
try {
if (null == str) {
return null;
}
return new String(Base64.getDecoder().decode(str));
} catch (UnsupportedEncodingException e) {
}
return null;
}
//解密加密后的字符串
public static String encodeData(String str) {
try {
if (null == str) {
return null;
}
return new String(Base64.getDecoder().encode(str));
} catch (UnsupportedEncodingException e) {
}
return null;
}
单向加密
1)MD5(Message Digest algorithm 5,信息摘要算法)
MD的作用是让大容量信息在用数字签名软件签署私人密钥前被”压缩”成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。广泛用于加密和解密技术,常用于文件校验。通常将MD5产生的字节数组交给BASE64再加密一把,得到相应的字符串。
//MD5加密
public static String encryptMD5(String str) throws NoSuchAlgorithmException{
//根据MD5算法生成MessageDigest对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
//更新摘要
md5.update(str.getBytes());
//完成哈希计算,得到bytes字节数组
byte[] bytes = md5.digest();
String data = byte2Hex(bytes);
return data;
}
//二进制转为十六进制
public static String byte2Hex(byte[] bytes) {
StringBuilder tokenBuilder = new StringBuilder();
String temp = null;
for (byte aByte : bytes) {
temp = Integer.toHexString(aByte & 0xFF);
if (temp.length() == 1) {
tokenBuilder.append("0");
}
tokenBuilder.append(temp);
}
return tokenBuilder.toString();
}
使用DigestUtils
String rowKey = DigestUtils.md5Hex(data)
2)SHA(Secure Hash Algorithm,安全散列算法)
SHA,安全散列算法,数字签名等密码学应用中重要的工具,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了, 但是SHA仍然是公认的安全加密算法,较之MD5更为安全。
public static byte[] encrypt(String str) throws NoSuchAlgorithmException {
// 根据SHA算法生成MessageDigest对象
MessageDigest md5 = MessageDigest.getInstance("SHA");
//更新摘要
md5.update(str.getBytes());
//完成哈希计算,得到bytes字节数组
byte[] bytes = md5.digest();
return bytes;
}
双向加密
1、对称加密
对称秘钥加密,就是采用这种加密方法的双方使用方式用同样的密钥进行加密和解密。密钥是控制加密及解密过程的指令。缺点是不具有个体原子性,一个密钥被共享,泄露几率会大大增加。有以下几种对称加密:
1)DES加密(Data Encryption Standard,数据加密标准)
2)AES加密(Advanced Encryption Standard,高级加密标准)
DES和AES的区别:
1)DES加密算法采用的是64位(8字节)分组加密方式,密钥的长度为56位(7个字节),8位用于校验。但推荐使用16个字节以上。
2)AES加密算法,密钥长度可选128位、192位或256位,分别对应16个字节、24个字节和32个字节。一般情况下,推荐使用最高的256位密钥,以确保更高的安全级别,需要注意Java默认只支持AES密钥长度为128位。
3)由于DES的密钥空间相对较小,容易受到暴力破解攻击,不再足够安全。但AES采用更强大的加密算法和更长的密钥长度,经过严密的审查和广泛的应用验证,被认为是目前最安全、最可靠的对称加密算法之一。
4)由于采用不同的加密方法和分组大小,AES比DES具有更高的加密效率,因此在实际应用中,通常使用AES加密算法来保证数据的机密性和安全性。
AES加密代码实例:(注意:把AES替换为DES即为DES加密)
public static void main(String[] args) throws Exception{
String str = "HelloWorld";
String key = "myKeyKeymyKeyKey";
//创建 AES 加密器
javax.crypto.Cipher encrypt = Cipher.getInstance("AES/ECB/PKCS5Padding");
javax.crypto.SecretKey encryptKey = new javax.crypto.spec.SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
//初始化加密器,并传入密钥
encrypt.init(Cipher.ENCRYPT_MODE, encryptKey);
//加密明文字符串
byte[] encryptBytes = encrypt.doFinal(str.getBytes(StandardCharsets.UTF_8));
//采用base64算法进行转码,避免出现中文乱码
String returnStr = org.apache.commons.codec.binary.Base64.encodeBase64String(encryptBytes);
System.out.println(returnStr);
//创建 AES 解密器
javax.crypto.Cipher decrypt = Cipher.getInstance("AES/ECB/PKCS5Padding");
javax.crypto.SecretKey decryptKey = new javax.crypto.spec.SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
//初始化解密器,并传入密钥
decrypt.init(Cipher.DECRYPT_MODE, decryptKey);
//采用base64算法进行转码,避免出现中文乱码
byte[] decodeStr = org.apache.commons.codec.binary.Base64.decodeBase64(returnStr);
//解密密文字节数组
byte[] decryptBytes = decrypt.doFinal(decodeStr);
System.out.println(new String(decryptBytes));
}
2、非对称加密
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥 (privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。有以下几种非对称加密:
1)RSA 公钥加密算法(1024位key的最多只能加密127位数据)
基于一个十分简单的数论事实: 将两个大素数相乘十分容易,RSA算法是第一个能同时用于加密和数字签名
的算法。
public static void main(String[] args) throws Exception{
String str = "Hello, RSA!";
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(str.getBytes());
//用于生成公钥和私钥对,基于RSA算法生成对象
java.security.KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
//初始化密钥对生成器,密钥大小为1024位
keyPairGen.initialize(1024, secureRandom);
//生成一个密钥对,保存在keyPair中,以此获得公钥和私钥
java.security.KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Map<String, Object> map = new HashMap<>();
map.put("publicKey", publicKey);
map.put("privateKey", privateKey);
Key key = (Key) map.get("publicKey");
byte[] publicBytes = key.getEncoded();
System.out.println("公钥是:" + Base64.encodeBase64String(publicBytes));
key = (Key) map.get("privateKey");
byte[] privateBytes = key.getEncoded();
System.out.println("私钥是:" + Base64.encodeBase64String(privateBytes));
//基于RSA算法生成对象使用公钥进行加密
javax.crypto.Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey, new SecureRandom());
byte[] encryptBytes = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
//采用base64算法进行转码,避免出现中文乱码
String returnStr = org.apache.commons.codec.binary.Base64.encodeBase64String(encryptBytes);
System.out.println("经过RSA加密之后返回的数据为:" + returnStr);
KeyFactory factory=KeyFactory.getInstance("RSA");
System.out.println("用私钥对信息进行数字签名================================");
//初始化私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(privateBytes);
//生成私钥
PrivateKey priKey = factory.generatePrivate(pkcs8KeySpec);
//实例化Signature
Signature signaturePri = Signature.getInstance("SHA256withRSA");
//初始化Signature
signaturePri.initSign(priKey);
//更新
signaturePri.update(str.getBytes());
//生成签名
byte[] signBytes = signaturePri.sign();
System.out.println("RSA产生签名:"+Base64.encodeBase64String(signBytes));
System.out.println("用公钥校验数字签名====================================");
//初始化公钥
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(publicBytes);
//生成公钥
PublicKey pubKey=factory.generatePublic(x509KeySpec);
//实例化Signature
Signature signaturePub = Signature.getInstance("SHA256withRSA");
//初始化Signature
signaturePub.initVerify(pubKey);
//更新
signaturePub.update(str.getBytes());
//验证
boolean verify = signaturePub.verify(signBytes);
System.out.println("经验证原数据(" + str + ")和签名匹配" + verify);
//初始化解密器,并传入私钥
cipher.init(Cipher.DECRYPT_MODE, privateKey, new SecureRandom());
//采用base64算法进行转码,避免出现中文乱码
byte[] decodeStr = org.apache.commons.codec.binary.Base64.decodeBase64(returnStr);
//解密密文字节数组
byte[] decryptBytes = cipher.doFinal(decodeStr);
System.out.println("经过RSA解密之后的原数据为:" + new String(decryptBytes));
}
2)DSA(Digital Signature Algorithm,数字签名算法)
DSA的一个重要特点是两个素数公开,DSA只是一种算法,和RSA不同之处在于 它不能用作加密和解密,也不能进行密钥交换,只用于签名
,它比RSA要快很多。
public static void main(String[] args) throws Exception{
String str = "Hello, DSA!";
//用于生成公钥和私钥对,基于RSA算法生成对象
java.security.KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");
//初始化密钥对生成器,密钥大小为1024位
keyPairGen.initialize(1024);
//生成一个密钥对,保存在keyPair中,以此获得公钥和私钥
java.security.KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Map<String, Object> map = new HashMap<>();
map.put("publicKey", publicKey);
map.put("privateKey", privateKey);
Key key = (Key) map.get("publicKey");
byte[] publicBytes = key.getEncoded();
System.out.println("公钥是:" + Base64.encodeBase64String(publicBytes));
key = (Key) map.get("privateKey");
byte[] privateBytes = key.getEncoded();
System.out.println("私钥是:" + Base64.encodeBase64String(privateBytes));
KeyFactory factory=KeyFactory.getInstance("DSA");
System.out.println("用私钥对信息进行数字签名");
//初始化私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(privateBytes);
//生成私钥
PrivateKey priKey = factory.generatePrivate(pkcs8KeySpec);
//实例化Signature
Signature signaturePri = Signature.getInstance("SHA1withDSA");
//初始化Signature
signaturePri.initSign(priKey);
//更新
signaturePri.update(str.getBytes());
//生成签名
byte[] signBytes = signaturePri.sign();
System.out.println("产生签名:"+Base64.encodeBase64String(signBytes));
System.out.println("用公钥校验数字签名");
//初始化公钥
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(publicBytes);
//生成公钥
PublicKey pubKey=factory.generatePublic(x509KeySpec);
//实例化Signature
Signature signaturePub = Signature.getInstance("SHA1withDSA");
//初始化Signature
signaturePub.initVerify(pubKey);
//更新
signaturePub.update(str.getBytes());
//验证
boolean verify = signaturePub.verify(signBytes);
System.out.println("经验证原数据(" + str + ")和签名匹配" + verify);
}
SM4算法参考
https://gitee.com/ld114631028/encrypt-spring-boot-starter/tree/master