需要考虑哪些问题?
在进行报文传输时,有两个问题需要考虑:
- 消息防篡改
- 加密报文
定义消息结构
为了方便后面使用,这里定义消息结构:
public static class Message {
public String data; //消息
public String sign; //签名
public Message(String data, String sign) {
this.data = data;
this.sign = sign;
}
}
对报文进行签名
首先我们假设消息是一个字符串:String msg = "Hello, message encryption!"
然后我们对这个报文计算摘要:byte[] msgHash = md5(msg)
。只要两个字符串计算出的摘要相同,我们认为这两个字符串是相等的(即没有被篡改过)。
然而如果直接传输 msg 及其摘要,那么很容易被别人篡改,这时就需要对摘要进行加密,也就是所谓的签名。也就是防止篡改的核心。下面给出一个完整的实现:
这里只放主要流程,辅助方法见附录:
// 生成RSA密钥对
KeyPair keyPair = generateKeyPair();
// 获取公钥和私钥
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 要加密的原始数据
String originalMessage = "Hello, message encryption!";
//发送端签名
Message message = sign(originalMessage, privateKey);
System.out.println("加密后的消息签名: " + message.sign);
//接收端校验签名
boolean checkResult = checkSign(message, publicKey);
System.out.println("合法:" + checkResult);
对报文进行加密
在发送端: 首先生成 AES 密钥,使用AES对报文进行加密,然后使用 RSA 对 AES 密钥进行加密。(考虑到报文本身可能较大,而非对称RSA加密效率较差)
在接收端:使用 RSA解密 AES 密钥,使用解密的 AES 密钥解密报文。
// 生成RSA密钥对
KeyPair keyPair = generateKeyPair();
// 获取公钥和私钥
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
//生成 aes 密钥
byte[] aesKey = generateAesKey();
//使用公钥对 aes 密钥进行加密
byte[] encryptedAesKey = encrypt(aesKey, publicKey);
//报文
String originalMessage = "Hello, message encryption!";
// 使用AES密钥加密报文
byte[] encryptedMessage = encryptWithAes(originalMessage.getBytes(), aesKey);
//将encryptedAesKey和encryptedMessage传给接收端
// 使用私钥解密AES密钥
byte[] decryptedAesKey = decrypt(encryptedAesKey, privateKey);
// 使用AES密钥解密消息
String decryptedMessage = new String(decryptWithAes(encryptedMessage, decryptedAesKey));
附录
//公钥签名
public static Message sign(String originalMessage, PublicKey publicKey) throws Exception {
byte[] bytes = calculateMD5(originalMessage);
byte[] encryptedHash = encrypt(bytes, publicKey);
String signStr = bytesToHex(encryptedHash);
return new Message(originalMessage, signStr);
}
//私钥签名
public static Message sign(String originalMessage, PrivateKey privateKey) throws Exception {
byte[] bytes = calculateMD5(originalMessage);
byte[] encryptedHash = encrypt(bytes, privateKey);
String signStr = bytesToHex(encryptedHash);
return new Message(originalMessage, signStr);
}
//公钥验签
public static boolean checkSign(Message message, PublicKey publicKey) throws Exception {
byte[] sign = hexToBytes(message.sign);
byte[] md5Hash = decrypt(sign, publicKey);
byte[] calcMd5Hash = calculateMD5(message.data);
return Arrays.equals(md5Hash, calcMd5Hash);
}
//私钥验签
public static boolean checkSign(Message message, PrivateKey privateKey) throws Exception {
byte[] sign = hexToBytes(message.sign);
byte[] md5Hash = decrypt(sign, privateKey);
byte[] calcMd5Hash = calculateMD5(message.data);
return Arrays.equals(md5Hash, calcMd5Hash);
}
// 生成RSA密钥对
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom();
keyPairGenerator.initialize(2048, secureRandom);
return keyPairGenerator.generateKeyPair();
}
// 计算MD5哈希值
public static byte[] calculateMD5(String message) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(message.getBytes());
return md.digest();
}
// 使用公钥加密数据
public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
// 使用私钥加密数据
public static byte[] encrypt(byte[] data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
// 使用公钥解密数据
public static byte[] decrypt(byte[] data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
// 使用私钥解密数据
public static byte[] decrypt(byte[] data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
// 生成AES密钥
public static byte[] generateAesKey() throws Exception {
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
return key;
}
// 使用AES密钥加密数据
public static byte[] encryptWithAes(byte[] data, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
return cipher.doFinal(data);
}
// 使用AES密钥解密数据
public static byte[] decryptWithAes(byte[] encryptedData, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
return cipher.doFinal(encryptedData);
}
// 将字节数组转换为十六进制字符串
public static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
public static byte[] hexToBytes(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i + 1), 16));
}
return data;
}