Java 提供了多种加密算法,支持对数据进行加密、解密以及消息摘要等操作。常见的加密算法可以分为 对称加密、非对称加密 和 哈希算法 三类。以下是对这些算法的简要介绍及其在 Java 中的实现方式。
1. 对称加密算法
对称加密算法使用相同的密钥进行加密和解密。常见的对称加密算法包括 AES(高级加密标准)、DES(数据加密标准)和 3DES(三重 DES)。
AES(高级加密标准)
AES 是现代加密算法中最为广泛使用的对称加密算法。它支持多种密钥长度(如 128、192、256 位)并具有高效性。
- 加密/解密过程:
- 使用相同的密钥进行加密和解密。
- AES 支持不同模式(如 ECB、CBC、CFB、OFB)和填充方式(如 PKCS5Padding、NoPadding 等)。
Java 示例代码(AES):
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;public class AESExample {public static void main(String[] args) throws Exception {// 生成 AES 密钥KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(128); // 支持 128、192、256 位密钥SecretKey secretKey = keyGenerator.generateKey();// 创建 AES Cipher 对象Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// 随机生成初始向量byte[] iv = new byte[16];IvParameterSpec ivSpec = new IvParameterSpec(iv);// 加密cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);String plaintext = "Hello, World!";byte[] encrypted = cipher.doFinal(plaintext.getBytes());System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));// 解密cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);byte[] decrypted = cipher.doFinal(encrypted);System.out.println("Decrypted: " + new String(decrypted));}
}
DES(数据加密标准)
DES 是一种较为古老的对称加密算法,已经不再推荐用于加密高安全性的数据,因其密钥长度较短(56 位)且容易被暴力破解。虽然它仍然可以用于一些较低安全要求的应用,但 AES 被广泛认为是更安全的替代方案。
Java 示例代码(DES):
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;public class DESExample {public static void main(String[] args) throws Exception {// 生成 DES 密钥KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");keyGenerator.init(56); // DES 密钥长度为 56 位SecretKey secretKey = keyGenerator.generateKey();// 创建 DES Cipher 对象Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");// 加密cipher.init(Cipher.ENCRYPT_MODE, secretKey);String plaintext = "Hello, World!";byte[] encrypted = cipher.doFinal(plaintext.getBytes());System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));// 解密cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decrypted = cipher.doFinal(encrypted);System.out.println("Decrypted: " + new String(decrypted));}
}
2. 非对称加密算法
非对称加密使用一对密钥,分别为 公钥(用于加密)和 私钥(用于解密)。常见的非对称加密算法有 RSA 和 DSA(数字签名算法)。
RSA(Rivest-Shamir-Adleman)
RSA 是一种常见的非对称加密算法,用于数据加密和数字签名。RSA 的安全性依赖于大数分解的难度。
- 加密/解密过程:
- 使用公钥加密数据,私钥解密。
- 加密的块大小必须小于密钥长度。
Java 示例代码(RSA):
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Base64;public class RSAExample {public static void main(String[] args) throws Exception {// 生成 RSA 密钥对KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");keyPairGenerator.initialize(2048); // 2048 位密钥KeyPair keyPair = keyPairGenerator.generateKeyPair();PublicKey publicKey = keyPair.getPublic();PrivateKey privateKey = keyPair.getPrivate();// 加密Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE, publicKey);String plaintext = "Hello, World!";byte[] encrypted = cipher.doFinal(plaintext.getBytes());System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));// 解密cipher.init(Cipher.DECRYPT_MODE, privateKey);byte[] decrypted = cipher.doFinal(encrypted);System.out.println("Decrypted: " + new String(decrypted));}
}
3. 哈希算法
哈希算法(又叫散列算法)用于生成固定长度的输出(哈希值),常用于数据完整性验证、数字签名等场景。常见的哈希算法有 MD5、SHA-1、SHA-256。
SHA-256
SHA-256 是一种安全哈希算法,生成 256 位的哈希值,常用于密码存储和文件完整性验证。
Java 示例代码(SHA-256):
import java.security.MessageDigest;
import java.util.Base64;public class SHA256Example {public static void main(String[] args) throws Exception {String input = "Hello, World!";// 创建 SHA-256 MessageDigest 对象MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");// 计算哈希值byte[] hash = messageDigest.digest(input.getBytes());// 输出哈希值System.out.println("SHA-256 Hash: " + Base64.getEncoder().encodeToString(hash));}
}
4. 常见加密算法总结
加密算法 | 类型 | 描述 |
---|---|---|
AES | 对称加密 | 高级加密标准,支持多种密钥长度 |
DES | 对称加密 | 数据加密标准,已不推荐使用 |
RSA | 非对称加密 | 公钥加密、私钥解密,广泛用于加密和数字签名 |
MD5 | 哈希算法 | 常用于数据完整性检查,已不再推荐用于安全应用 |
SHA-1 | 哈希算法 | 安全哈希算法,已被认为不够安全 |
SHA-256 | 哈希算法 | 安全哈希算法,生成 256 位哈希值 |
总结
- 对称加密:算法简单,性能高,但密钥管理较为困难。常见的如 AES、DES。
- 非对称加密:使用公钥和私钥,安全性较高,但性能相对较差。常见的如 RSA。
- 哈希算法:用于验证数据完整性,生成固定长度的哈希值,常见的如 SHA-256。
Java 提供了强大的加密支持,可以通过 javax.crypto
、java.security
包中的类来实现这些加密算法。
实战
package com.smcv.mall.tasks.util;import com.alibaba.fastjson.JSON;
import com.smcv.mall.tasks.model.dto.lbs.req.SendConfigReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;@Component
@Slf4j
public class LovPlatformReqSignUtils {private LovPlatformReqSignUtils() {}/*** pro xxxpro* uat b82bf8347c2f4800b51bd6a5d04013c9* <p>* 线下分配密钥*/@Value("${smcv.xxx.lov.secret:b84800b51bd6a5d04013c9}")private String secret;public String signature(String path, SendConfigReqDTO body, String timestamp) {// 过滤掉这些特殊字符 { } " ' , :String param = path + JSON.toJSONString(body).replaceAll("[{}\"',:]", "");// 对字符进⾏排序char[] chars = param.toCharArray();Arrays.sort(chars);// 整体结构:secret + 请求参数 + timestampString str = secret + new String(chars) + timestamp;log.info("param: {}, str: {}", param, str);// 使⽤sha1计算得到⼀个签名值return sha1(str);}/*** 加签** @param path url路径参数,从(?,结尾]* @param body 消息体,此处⽤map代替,⼀般为实体类* @param timestamp 与header中时间戳保持⼀致* @return 签名*/public String signature(String path, Map<String, Object> body, String timestamp) {// 过滤掉这些特殊字符 { } " ' , :String param = path + JSON.toJSONString(body).replaceAll("[{}\"',:]", "");// 对字符进⾏排序char[] chars = param.toCharArray();Arrays.sort(chars);// 整体结构:secret + 请求参数 + timestampString str = secret + new String(chars) + timestamp;// 使⽤sha1计算得到⼀个签名值return sha1(str);}/*** SHA1加密,可⽤⼯具类代替** @param input* @return*/private String sha1(String input) {try {MessageDigest md = MessageDigest.getInstance("SHA-1");byte[] result = md.digest(input.getBytes());StringBuilder sb = new StringBuilder();for (byte b : result) {sb.append(String.format("%02x", b));}return sb.toString();} catch (Exception e) {e.printStackTrace();}return null;}
}
一般加密串,为什么拼接时间戳
在加密和数据传输过程中,拼接 时间戳 是一种常见的做法,它有助于提高安全性和防止各种攻击。以下是将时间戳拼接到加密串中的主要原因:
1. 防止重放攻击 (Replay Attack)
重放攻击 是指攻击者截获并重新发送已经有效的消息,通常用于绕过认证或授权过程。为了防止这种攻击,通常会在消息中加入 时间戳。
- 时间戳可以确保每个消息只能在特定时间内有效。如果消息的时间戳超出了允许的时间窗口(例如 5 分钟),接收方会认为这条消息是重放的,进而拒绝该消息。
- 通过在每次请求时加入不同的时间戳,确保同一请求不会被攻击者重复发送。
示例: 假设某个 API 接口要求请求中携带时间戳。如果攻击者截获了有效请求并稍后再次发送(重放请求),由于时间戳不再有效,接收方会拒绝这个请求。
2. 增加唯一性
拼接时间戳可以确保每次生成的加密字符串都是唯一的,即使消息内容完全相同。即使其他部分(如用户ID、请求内容)不变,时间戳的不同也能确保生成的加密串不会重复。这对于防止 碰撞攻击(即两个不同的输入生成相同的加密输出)是有帮助的。
3. 防止缓存中间人攻击(Man-in-the-Middle Attack)
在一些情况下,攻击者可能在数据传输过程中进行中间人攻击(MITM),修改消息内容或发送伪造的消息。时间戳可以有效避免这种攻击,因为加密串通常是基于特定时间戳生成的,攻击者无法在不重新计算加密值的情况下修改时间戳。
4. 确保消息的时效性
在某些系统中,特别是金融交易或敏感操作,消息的时效性非常重要。时间戳可以确保处理的消息是在一个合理的时间范围内生成的。如果消息的时间戳过旧,接收方可以拒绝消息处理,避免处理过时的数据。
5. 避免缓存攻击
时间戳的引入能够避免缓存攻击,即攻击者通过保存某一时刻的请求及其加密结果,在稍后重新提交来获取响应。由于时间戳会随请求的生成而变化,每次请求都会有不同的加密串,防止了攻击者重复使用缓存中的加密数据。
6. 增强安全性与一致性
时间戳是一个简单但有效的随机因素,结合加密技术使用时能显著增强加密信息的安全性。它增加了每个请求的不可预测性,即使攻击者知道加密算法和密钥,也无法推测出有效的请求内容。
示例:时间戳与加密的结合
假设我们要加密一个用户请求,在请求中包含时间戳。通常,时间戳和请求内容会一起拼接并进行加密,这样可以确保每个请求的加密串是唯一的,并且与时间相关。
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Base64;public class EncryptWithTimestampExample {public static void main(String[] args) throws Exception {// 生成时间戳SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");sdf.setTimeZone(TimeZone.getTimeZone("UTC"));String timestamp = sdf.format(new Date());// 请求内容String content = "userID=123&action=login";// 拼接时间戳String dataToEncrypt = content + "×tamp=" + timestamp;// 加密操作String encryptedData = encrypt(dataToEncrypt);System.out.println("Encrypted Data: " + encryptedData);// 解密操作(模拟验证)String decryptedData = decrypt(encryptedData);System.out.println("Decrypted Data: " + decryptedData);}// 加密方法private static String encrypt(String data) throws Exception {// 使用 AES 算法生成密钥KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(128); // 使用 128 位密钥SecretKey secretKey = keyGenerator.generateKey();Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(encrypted);}// 解密方法private static String decrypt(String encryptedData) throws Exception {// 使用 AES 算法生成密钥(这里应该与加密时使用的密钥相同)KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(128);SecretKey secretKey = keyGenerator.generateKey();Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedData));return new String(decrypted, StandardCharsets.UTF_8);}
}
总结
拼接时间戳到加密串的主要目的是为了:
- 防止重放攻击,确保每个请求是唯一且及时的;
- 增加数据的唯一性和不可预测性,避免碰撞和缓存攻击;
- 提高消息的时效性,确保只有在合理的时间内传输的消息有效。
通过这些方法,结合加密技术使用时间戳可以显著增强系统的安全性,确保通信的完整性和保密性。