概述
本文介绍了业界常用的RSA非对称加减密、3 DES对称加减密、加签验签方法。
业界典型的加密通信方式
在业界比较常见的做法是:对报文进行对称加密,对称加密的密钥,用对方的RSA公钥加密。为了报文的完整性,还需要对报文进行加签。
其中,RSA公私钥对的生成,是在企业内部自主完成。企业自主生成的RSA公钥,需要发送给报文接收方。公钥用来加密报文,私钥用来解密报文。比如企业A将公钥发给企业B,企业A保管自己的私钥,企业B用公钥对报文加密,发送给企业A,企业A用私钥进行解密。如果企业B向企业A发送消息,那么企业A向企业B提供公钥,因此我们总结为:
△ 非对称加密如RSA,生成时会有一把公钥,一把私钥,私钥自己保存,公钥发给对方 △ 公钥用于加密,私钥用于解密 △ A要发消息给B,需要B的公钥;B要发消息给A,需要A的公钥
实践案例
本案例中,我们使用到的算法:
- 对称加密密钥算法:
3 DES
,用于对报文加密,其优点是安全性高,速度快 - 非对称加密算法:
RSA/ECB/PKCS1Padding
,用于对密钥加密 - 签名算法:
SHA256withRSA
,加签用自己的私钥,验签用对方公钥
具体流程如图:
在两个企业已经互发了RSA公钥的前提下,消息发送方(企业A)加密流程为:
- 企业A实时生成对称加密密钥K,加密算法为3 DES
- 用企业B的公钥对K进行加密,得到加密后的解密密钥encrypted_key
- 用K对明文C进行加密,得到encrypted_data
- 用企业A的私钥对报文进行签名(加签),签名算法为SHA256withRSA,得到签名串sign
- 打包报文[encrypted_key, encrypted_data,sign],发送给接收方企业B
消息接受方(企业B)解密流程为:
- 用企业B的RSA私钥对encrypted_key进行解密,得到解密密钥K
- 用解密密钥K对encrypted_data解密,得到明文的C
- 用企业A的公钥对报文进行签名串sign验签,验签算法为SHA256withRSA
算法选型解惑
- RSA为什么不用来加密报文体,而是用来加密解密密钥?
- 密钥长度:RSA的安全性取决于密钥的长度。较长的密钥长度提供更高的安全性,但也导致更慢的性能。常用的RSA密钥长度为2048位或4096位。
- 加密速度:RSA的加密速度比对称加密算法(如AES)慢得多。这是因为RSA算法涉及大数的运算,包括模幂运算和大素数计算,这些运算需要较长的时间。因此,对大量数据进行RSA加密可能会显著降低性能。
- 加密限制:RSA加密的数据长度有限制。加密的数据块不能超过密钥长度,且通常要小于密钥长度减去一些填充的字节数。对于较长的数据,通常使用对称加密算法加密数据本身,然后使用RSA加密对称密钥。
基于以上原因,我们不用RSA算法来直接加密报文体本身。
- 为什么用3 DES来加密报文体?
- 兼容性:3DES是DES算法的增强版本,它使用相同的加密算法结构,只是增加了迭代次数。这意味着3DES可以与使用DES加密的旧系统兼容,同时提供更高的安全性。
- 安全性:DES算法的密钥长度较短(56位),已被认为不够安全。3DES通过使用三个不同的密钥对数据进行三次加密,有效地扩展了密钥长度,提供更高的安全性。它提供的密钥空间更大(168位),使得暴力破解变得非常困难。
- 抗击穷举攻击:3DES的安全性基于DES算法的困难性。对于穷举攻击(exhaustive search attack),即尝试所有可能的密钥进行解密的攻击,3DES提供了更大的密钥空间,使得攻击者需要更多的时间和计算资源来尝试所有可能的密钥。
- 广泛应用:由于3DES的兼容性和安全性,它被广泛应用于各种领域,如金融机构、电子支付、通信和网络安全等。许多安全标准和协议(如TLS、IPsec)也使用3DES作为加密算法之一。
最佳实践(Java)
生成企业A和企业B自己的RSA公私钥对(Base 64编码)
生成方法:
public class RSAKeyPairGenerator {
public static void main(String[] args) {
try {
// 创建RSA密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
// 设置密钥长度,一般推荐使用2048位或以上
keyPairGenerator.initialize(2048, new SecureRandom());
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公钥和私钥的字节数组
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
// 将字节数组转Base 64编码
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKeyBytes);
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKeyBytes);
// 输出公钥和私钥
System.out.println("公钥:" + publicKeyBase64);
System.out.println( "私钥:" + privateKeyBase64);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
通过以上方法,我们得到企业A和企业B的公私钥信息。
- 企业A公钥:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtLmnzL7sqrv5LsUt2LYs5nEKdlt+BIgbTT4SYutPgCNFR83AE95w7/cQHCG1T79Agp3WbwpJOT6tCLUBQ48V2rRA235ZOtlswMKM1WE9TM6AuyXCGJLIFuK9pShCx7+6jksSNtuGxf2YfTFxKnP7VfjxEZKn26aCiSqzXBptxesVfPgHIB4OSRZFT360jBP/38ci37Q3vHND2z5plPMfdNRqgGawMb0lNsnCFi28l5jetpzQ25mfTUxaP0dT+m4+uXHm6vAwrYal0i/p0UWwHgzVZUvKirZfHURYzZ4BWC9grbSAA7XMLaxhdR/J3pEGT6rA0onrhQUjafWTsVpVYwIDAQAB
- 企业A私钥:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0uafMvuyqu/kuxS3YtizmcQp2W34EiBtNPhJi60+AI0VHzcAT3nDv9xAcIbVPv0CCndZvCkk5Pq0ItQFDjxXatEDbflk62WzAwozVYT1MzoC7JcIYksgW4r2lKELHv7qOSxI224bF/Zh9MXEqc/tV+PERkqfbpoKJKrNcGm3F6xV8+AcgHg5JFkVPfrSME//fxyLftDe8c0PbPmmU8x901GqAZrAxvSU2ycIWLbyXmN62nNDbmZ9NTFo/R1P6bj65cebq8DCthqXSL+nRRbAeDNVlS8qKtl8dRFjNngFYL2CttIADtcwtrGF1H8nekQZPqsDSieuFBSNp9ZOxWlVjAgMBAAECggEATxA1n7YbFap8ngB3SSqRxBLKD/OA0vy0bPtt0ca+FgHYKkyR7l9PKzv72ULJ7mRuFXZ1vEhUtm18B6YgMm0jk3VuNlg1MzCxijbSo/sRdUuWN8mR+2wjixeQ/fL6HCb9t4iSJFuvv2htUL/TkU4CM59f3184fjh/PQDoTK/nPEEaA15g+6VuAt76r04LA4XhYOQtXnR+sHVIc/b/FqgEFXLaIPvQqXDCZmZhewOd2Hzgxl6a5cDQ1ZLLmnczMiLi2Yu2BGdHl4XVwjmC7bWO/hliaAvLyYq83eap+AJtXiL99fHWD4qETcp2hSC+4v03Zgh2qUT9APw16UVGQhyauQKBgQDunRVL4FoY+VqqXo5EuCh9nQNzTYAh5587t2P4VS1mr+8v5qqeD6IWt/eiOt0078nhAS3f3zZ974w8pwam7Y4pb47OjmLj/F655E6t9HRrcPhGffuj8X4VtUAznUckYHnJFWc0ivMyTS14lJXiAWsKHJqBLLvEM5fKLDY5IYxpPwKBgQDB5MQqLCCNx0ONK5hwmX2DyigyKMgG0oQTDtAVmy0oAEFViBCc11Tazf7C0HA1+SBZQ2YylUwCGkvJ3cQII8b1GJzNTefrOeFPOg8CxztkBP/o/f2a0FeULhSzWTZSrArzkB/4xN+sxtF/wtItDBYrLA43L3uc0EBGyjJ4GFQG3QKBgAv06yBGOb2OoTBwVIi1AbJpr8aTV5xbUutXITdMbV376uxnjp0lA2ZICayjbno8mc0glf/l24EsA8b2R6sNtFX3NRy8nc/20XhXLGojGcJYgthlb7qjW0ITNwhfFTWJcBDXdbh4Tf9zRpgIOgp9L7PuAv/+EdqfCzgiRwVN3Q9ZAoGALnCtr3AsR+kPO/PSKj4JOxSjdgggiMnJSotEubgWI7uT6nmRiQ9mSpYGTAECr1rBLcAf7UOnFeXYezw7pLt0/uVrQR1EzClr1r8QM1km13j/Fn4nTPpTz7EN0jsu8LQ2V0oQ7lOxQz/Hk/NExfdcI/EEYqFj2RZkHtB1MCCbl50CgYEAvJ7dmIRShffxkllybCbM3DEYidbszorGp69tUyX2Hb4EYH7qsH1RXtRivltzsR+b9f9lrfTwrSdD7rMpwRHAYHbDSKQgL/sVDNh8DnFn/dDFoLpfPQdR9PfNTUPtqe8NIjIUhkxzLOqHZgm+zGfOMlfgJiRodKJ63lidHEXHBwE=
- 企业B公钥:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApAkZ0gbyKfDDYtMs8ITRITYOF8OyRDHyeJmUQyDjXlMqO3c911025N02JEkk68UI7n6cYARiBYPznrKTckzbzOxlrwTyOveQpxeR/KsqQglXTGglmEUHyOfKRTajkhtwv3bpt5OPRaFdwOZjGYUJ6dOqdnlH9LASIjA0jNDFoT4RANgYbdS+s+Y5/uS+lZD7RnRjwdcHJWpyO3Jp7OKukRN+EaedBFuuE2iF8XWcKXVGUXZyxqOU0S266WYJdcmZaRkKMZ4Pruy5IjwEXDR7sMVv+51wVv85ue+kGDeqlVfHajLh6G5mWvqNuFASyx2obYUNeE3f53ziC6qjHuTcXQIDAQAB
- 企业B的私钥:
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCkCRnSBvIp8MNi0yzwhNEhNg4Xw7JEMfJ4mZRDIONeUyo7dz3XXTbk3TYkSSTrxQjufpxgBGIFg/OespNyTNvM7GWvBPI695CnF5H8qypCCVdMaCWYRQfI58pFNqOSG3C/dum3k49FoV3A5mMZhQnp06p2eUf0sBIiMDSM0MWhPhEA2Bht1L6z5jn+5L6VkPtGdGPB1wclanI7cmns4q6RE34Rp50EW64TaIXxdZwpdUZRdnLGo5TRLbrpZgl1yZlpGQoxng+u7LkiPARcNHuwxW/7nXBW/zm576QYN6qVV8dqMuHobmZa+o24UBLLHahthQ14Td/nfOILqqMe5NxdAgMBAAECggEAFM/eycn90Zptf81hR0birb8I/LTkmkhoBiseJ4I1ttMAabkQTJHUFCeI5gOtvogqQPApcPk9YovCTiFEGd1sicx4fkM3ZInifYC9Efy5iNVckSIpmu8vNKtL+4oaKMB4hqJUtuju5Zu6pG3Wpvc9McCcQRnUKlRoiJPrJu6aFIHx4rqSjoI3cmC7k/4Z5M12HQAtAdj97+0+LGzytnWf3JgopH4k/HFT0XTb13JTBdaRsjWFLJwKqXcBYNeDOUAoK+bcRd7V5eYqoyIfIBwIT2xNJU4ivK0copoIqKAbcu2vY/KcSrnCx+G7yflJ38Qiv/1HhXNpUPbElt2hsN9roQKBgQDtFBKv8Ao9ax9ls9ukcfhlCrGgtTTUMoXEEz/d2WKVzX9Sc5lkczsqAMNZ++x630c61/JNNPkzDBNrY8PWMvGvTukwNRJnqUYdWhIMtJUJkf+Y6I7CTShDy9eezxMUe9X8sY3J25s8Xz1Qv6yt91+ZweUvoYy2WFbdQH50KnN7uQKBgQCxIKJky1oxV02OyGm/kIMZf4QEKii3nZsQUYqoS8Uc6eDLM3G2URQxWlQ4ugSTC6G6imGXU2AVL2xNniy+NqPHICikTQ1XZaz+8KBeeotznWJJL5bCpCcHrC//C9oMA/t0huRTFzLeEmIV+OHWCCxyO+z5XxWujPXB31Tt4oVfxQKBgDdY7FPusjVMgPP5XVmy0c6lBHsEUuUHNntkExDz4zQd2Y+iNTliPXm8295yFe9JmRN/vq2PpG3qb84uaFXlZs8KmR4MBdP1jMzlbjlRH0owr38/K7To1nGdcSU+KrIphveLbBKoFkGt6l6joOisS8FVpu/Lw1H+pajZmav9DSDhAoGBAK2YVFAA1MZiz8pONQXgNfx5cwM55mn+dwjJQeGrUOYDeaLtKlcKo4WzB7QI647J6ZmPIhJTTmm07qoriaJqnpz7sZlFQvwS1DeP0TyHUcDf0IH1uAXPJ8lnQirujKcWCA2uXnvo0pu+3I64O22u2RCkFp5YSEGoOPvnS69RQHIZAoGBAM5MotoO3IWn5Kw54hhQkfmLCZmN2xeztSwmweIMNsn9Fjk2j1kZnqqusxAC3errKnQb+Xmo3P/pJmYW9N5m7HT9kcuK7vJAdq6eIcVkCKo17RLKUhYhQd3HgqrjUQRsjYWkUKdW7UDM8NoyG5JXYOaQ+BFjcParOKXS4QKiRgAh
企业A加密过程
在上面RSA公私钥对的基础上,我们来模拟企业A加密的流程,假设我们要发给企业B的原报文内容为:
Hello, World!
注:本文所有加减密信息输出均为Base64编码。
企业A生成 3 DES的加密串:
// 生成3DES密钥
public static String generateTripleDESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
byte[] keyBytes = keyGenerator.generateKey().getEncoded();
// 将SecretKey转换为Base 64字符串
return Base64.getEncoder().encodeToString(keyBytes);
}
3 DES密钥串:
lLkL6UwL+/uoy1RUhdPsV67NIP4a1eA7
对报文加密:
// 待加密的报文
String plaintext = "Hello, World!";
String encryptedText = encrypt(plaintext, secretKey);
System.out.println("Encrypted Text: " + encryptedText);
// 加密方法
public static String encrypt(String plaintext, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
加密后的字符串 encrypted_data:
KiKj8YAQeyX6U9CP0xuJTA==
对3 DES密钥串使用企业B的公钥加密:
package encrypt;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RsaEncrypt {
public static void main(String[] args) throws Exception {
String desKey = "lLkL6UwL+/uoy1RUhdPsV67NIP4a1eA7";
String publicKeyStrOfEntB = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApAkZ0gbyKfDDYtMs8ITRITYOF8OyRDHyeJmUQyDjXlMqO3c911025N02JEkk68UI7n6cYARiBYPznrKTckzbzOxlrwTyOveQpxeR/KsqQglXTGglmEUHyOfKRTajkhtwv3bpt5OPRaFdwOZjGYUJ6dOqdnlH9LASIjA0jNDFoT4RANgYbdS+s+Y5/uS+lZD7RnRjwdcHJWpyO3Jp7OKukRN+EaedBFuuE2iF8XWcKXVGUXZyxqOU0S266WYJdcmZaRkKMZ4Pruy5IjwEXDR7sMVv+51wVv85ue+kGDeqlVfHajLh6G5mWvqNuFASyx2obYUNeE3f53ziC6qjHuTcXQIDAQAB";
PublicKey publicKey = convertStringToPublicKey(publicKeyStrOfEntB);
String encryptedText = encrypt(desKey, publicKey);
System.out.println("encrypted_key: " + encryptedText);
}
// 加密
public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// 将字符串转换为公钥对象
public static PublicKey convertStringToPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
return keyFactory.generatePublic(keySpec);
}
}
得到 encrypted_key:
DmB1O9/0KfG53qvgQ/jiCYhDwFISu+RiVqWQF3o1IhRcxYe+e8GJMIVh8/6Wrc4jzywlg6Eylsc0jTvvEbprH4USQc/I4aYVPaY0kSlRcpIEgy69S9D4Zlc5XsLiOiXrZP2O/9FsdphFIeKuzN4dT8tnudfNXrS9A21s2bpF0bt4eDvLNUxbl3mnI74VAmH5o723tEmqFq5XeO/gBudxeHlIwTO09/TjTpM3wGwQwycVr/CZRgY4SWoCkiZhRH7ikfpoqPODoYL0ZL083U2ClR0BZ71jZh3aPqTsjTIk/ctAEW+eC92a7/d7CWpx+DKvIngNvPSgXXPq4JCikmH8pQ==
用SHA246withRSA
对明文报文加签:
package encrypt;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class RSASignatureExample {
public static void main(String[] args) {
try {
// 企业A的私钥
String privateKeyOfEntA = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0uafMvuyqu/kuxS3YtizmcQp2W34EiBtNPhJi60+AI0VHzcAT3nDv9xAcIbVPv0CCndZvCkk5Pq0ItQFDjxXatEDbflk62WzAwozVYT1MzoC7JcIYksgW4r2lKELHv7qOSxI224bF/Zh9MXEqc/tV+PERkqfbpoKJKrNcGm3F6xV8+AcgHg5JFkVPfrSME//fxyLftDe8c0PbPmmU8x901GqAZrAxvSU2ycIWLbyXmN62nNDbmZ9NTFo/R1P6bj65cebq8DCthqXSL+nRRbAeDNVlS8qKtl8dRFjNngFYL2CttIADtcwtrGF1H8nekQZPqsDSieuFBSNp9ZOxWlVjAgMBAAECggEATxA1n7YbFap8ngB3SSqRxBLKD/OA0vy0bPtt0ca+FgHYKkyR7l9PKzv72ULJ7mRuFXZ1vEhUtm18B6YgMm0jk3VuNlg1MzCxijbSo/sRdUuWN8mR+2wjixeQ/fL6HCb9t4iSJFuvv2htUL/TkU4CM59f3184fjh/PQDoTK/nPEEaA15g+6VuAt76r04LA4XhYOQtXnR+sHVIc/b/FqgEFXLaIPvQqXDCZmZhewOd2Hzgxl6a5cDQ1ZLLmnczMiLi2Yu2BGdHl4XVwjmC7bWO/hliaAvLyYq83eap+AJtXiL99fHWD4qETcp2hSC+4v03Zgh2qUT9APw16UVGQhyauQKBgQDunRVL4FoY+VqqXo5EuCh9nQNzTYAh5587t2P4VS1mr+8v5qqeD6IWt/eiOt0078nhAS3f3zZ974w8pwam7Y4pb47OjmLj/F655E6t9HRrcPhGffuj8X4VtUAznUckYHnJFWc0ivMyTS14lJXiAWsKHJqBLLvEM5fKLDY5IYxpPwKBgQDB5MQqLCCNx0ONK5hwmX2DyigyKMgG0oQTDtAVmy0oAEFViBCc11Tazf7C0HA1+SBZQ2YylUwCGkvJ3cQII8b1GJzNTefrOeFPOg8CxztkBP/o/f2a0FeULhSzWTZSrArzkB/4xN+sxtF/wtItDBYrLA43L3uc0EBGyjJ4GFQG3QKBgAv06yBGOb2OoTBwVIi1AbJpr8aTV5xbUutXITdMbV376uxnjp0lA2ZICayjbno8mc0glf/l24EsA8b2R6sNtFX3NRy8nc/20XhXLGojGcJYgthlb7qjW0ITNwhfFTWJcBDXdbh4Tf9zRpgIOgp9L7PuAv/+EdqfCzgiRwVN3Q9ZAoGALnCtr3AsR+kPO/PSKj4JOxSjdgggiMnJSotEubgWI7uT6nmRiQ9mSpYGTAECr1rBLcAf7UOnFeXYezw7pLt0/uVrQR1EzClr1r8QM1km13j/Fn4nTPpTz7EN0jsu8LQ2V0oQ7lOxQz/Hk/NExfdcI/EEYqFj2RZkHtB1MCCbl50CgYEAvJ7dmIRShffxkllybCbM3DEYidbszorGp69tUyX2Hb4EYH7qsH1RXtRivltzsR+b9f9lrfTwrSdD7rMpwRHAYHbDSKQgL/sVDNh8DnFn/dDFoLpfPQdR9PfNTUPtqe8NIjIUhkxzLOqHZgm+zGfOMlfgJiRodKJ63lidHEXHBwE=";
PrivateKey privateKey = convertStringToPrivateKey(privateKeyOfEntA);
// 需要签名的字符串
String plaintext = "Hello, World!";
// 对字符串进行签名
String signature = sign(plaintext , privateKey);
System.out.println("sign: " + signature);
} catch (Exception e) {
e.printStackTrace();
}
}
// 对字符串进行签名
public static String sign(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = signature.sign();
return Base64.getEncoder().encodeToString(signatureBytes);
}
// 将字符串转换为私钥对象
public static PrivateKey convertStringToPrivateKey(String privateKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
return keyFactory.generatePrivate(keySpec);
}
}
得到签名串 sign :
W2vPVeQtfIGLSYajA9PZzHuC0W7KPhfOXdC+osyBLYpYEm40NGshnKd8S8t5XMq6Tf/ZJ96fHeyS6XOIVjZCE0WZcl5J7s8uvkdjKKir35Itbj3hYidTVO1B93KdoAJRtjUd0NxaWN+uscJB5VepkNqayS4+wwyOf9HRQkcoRjNTzkHupUZlQ9bTedjIm3NK5U6qONbwqlEvgdYLWOW1DmXBdZ3UKbgTrlEmRYYHcI9xth1nbJr9YumENttHL0vzDFid1V3J5JqWN15jVfGPrUhSgZWeG8dQj3mP6uMQ5l2KRVLU7Ditvjy7MmINXmtHvKoeKyC5bO89y2em0TFfig==
因此我们得到企业A最终要发送的报文:
"encrypted_key":"DmB1O9/0KfG53qvgQ/jiCYhDwFISu+RiVqWQF3o1IhRcxYe+e8GJMIVh8/6Wrc4jzywlg6Eylsc0jTvvEbprH4USQc/I4aYVPaY0kSlRcpIEgy69S9D4Zlc5XsLiOiXrZP2O/9FsdphFIeKuzN4dT8tnudfNXrS9A21s2bpF0bt4eDvLNUxbl3mnI74VAmH5o723tEmqFq5XeO/gBudxeHlIwTO09/TjTpM3wGwQwycVr/CZRgY4SWoCkiZhRH7ikfpoqPODoYL0ZL083U2ClR0BZ71jZh3aPqTsjTIk/ctAEW+eC92a7/d7CWpx+DKvIngNvPSgXXPq4JCikmH8pQ==",
"encrypted_data":"KiKj8YAQeyX6U9CP0xuJTA==",
"sign":"W2vPVeQtfIGLSYajA9PZzHuC0W7KPhfOXdC+osyBLYpYEm40NGshnKd8S8t5XMq6Tf/ZJ96fHeyS6XOIVjZCE0WZcl5J7s8uvkdjKKir35Itbj3hYidTVO1B93KdoAJRtjUd0NxaWN+uscJB5VepkNqayS4+wwyOf9HRQkcoRjNTzkHupUZlQ9bTedjIm3NK5U6qONbwqlEvgdYLWOW1DmXBdZ3UKbgTrlEmRYYHcI9xth1nbJr9YumENttHL0vzDFid1V3J5JqWN15jVfGPrUhSgZWeG8dQj3mP6uMQ5l2KRVLU7Ditvjy7MmINXmtHvKoeKyC5bO89y2em0TFfig=="
企业B解密过程
企业B接收到报文后,首先用企业B的私钥对encrypted_key进行解密:
package encrypt;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class RsaDecode {
public static void main(String[] args) throws Exception {
String encryptedKey = "DmB1O9/0KfG53qvgQ/jiCYhDwFISu+RiVqWQF3o1IhRcxYe+e8GJMIVh8/6Wrc4jzywlg6Eylsc0jTvvEbprH4USQc/I4aYVPaY0kSlRcpIEgy69S9D4Zlc5XsLiOiXrZP2O/9FsdphFIeKuzN4dT8tnudfNXrS9A21s2bpF0bt4eDvLNUxbl3mnI74VAmH5o723tEmqFq5XeO/gBudxeHlIwTO09/TjTpM3wGwQwycVr/CZRgY4SWoCkiZhRH7ikfpoqPODoYL0ZL083U2ClR0BZ71jZh3aPqTsjTIk/ctAEW+eC92a7/d7CWpx+DKvIngNvPSgXXPq4JCikmH8pQ==";
String privateKeyStrOfEntB = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCkCRnSBvIp8MNi0yzwhNEhNg4Xw7JEMfJ4mZRDIONeUyo7dz3XXTbk3TYkSSTrxQjufpxgBGIFg/OespNyTNvM7GWvBPI695CnF5H8qypCCVdMaCWYRQfI58pFNqOSG3C/dum3k49FoV3A5mMZhQnp06p2eUf0sBIiMDSM0MWhPhEA2Bht1L6z5jn+5L6VkPtGdGPB1wclanI7cmns4q6RE34Rp50EW64TaIXxdZwpdUZRdnLGo5TRLbrpZgl1yZlpGQoxng+u7LkiPARcNHuwxW/7nXBW/zm576QYN6qVV8dqMuHobmZa+o24UBLLHahthQ14Td/nfOILqqMe5NxdAgMBAAECggEAFM/eycn90Zptf81hR0birb8I/LTkmkhoBiseJ4I1ttMAabkQTJHUFCeI5gOtvogqQPApcPk9YovCTiFEGd1sicx4fkM3ZInifYC9Efy5iNVckSIpmu8vNKtL+4oaKMB4hqJUtuju5Zu6pG3Wpvc9McCcQRnUKlRoiJPrJu6aFIHx4rqSjoI3cmC7k/4Z5M12HQAtAdj97+0+LGzytnWf3JgopH4k/HFT0XTb13JTBdaRsjWFLJwKqXcBYNeDOUAoK+bcRd7V5eYqoyIfIBwIT2xNJU4ivK0copoIqKAbcu2vY/KcSrnCx+G7yflJ38Qiv/1HhXNpUPbElt2hsN9roQKBgQDtFBKv8Ao9ax9ls9ukcfhlCrGgtTTUMoXEEz/d2WKVzX9Sc5lkczsqAMNZ++x630c61/JNNPkzDBNrY8PWMvGvTukwNRJnqUYdWhIMtJUJkf+Y6I7CTShDy9eezxMUe9X8sY3J25s8Xz1Qv6yt91+ZweUvoYy2WFbdQH50KnN7uQKBgQCxIKJky1oxV02OyGm/kIMZf4QEKii3nZsQUYqoS8Uc6eDLM3G2URQxWlQ4ugSTC6G6imGXU2AVL2xNniy+NqPHICikTQ1XZaz+8KBeeotznWJJL5bCpCcHrC//C9oMA/t0huRTFzLeEmIV+OHWCCxyO+z5XxWujPXB31Tt4oVfxQKBgDdY7FPusjVMgPP5XVmy0c6lBHsEUuUHNntkExDz4zQd2Y+iNTliPXm8295yFe9JmRN/vq2PpG3qb84uaFXlZs8KmR4MBdP1jMzlbjlRH0owr38/K7To1nGdcSU+KrIphveLbBKoFkGt6l6joOisS8FVpu/Lw1H+pajZmav9DSDhAoGBAK2YVFAA1MZiz8pONQXgNfx5cwM55mn+dwjJQeGrUOYDeaLtKlcKo4WzB7QI647J6ZmPIhJTTmm07qoriaJqnpz7sZlFQvwS1DeP0TyHUcDf0IH1uAXPJ8lnQirujKcWCA2uXnvo0pu+3I64O22u2RCkFp5YSEGoOPvnS69RQHIZAoGBAM5MotoO3IWn5Kw54hhQkfmLCZmN2xeztSwmweIMNsn9Fjk2j1kZnqqusxAC3errKnQb+Xmo3P/pJmYW9N5m7HT9kcuK7vJAdq6eIcVkCKo17RLKUhYhQd3HgqrjUQRsjYWkUKdW7UDM8NoyG5JXYOaQ+BFjcParOKXS4QKiRgAh";
PrivateKey privateKey = convertStringToPrivateKey(privateKeyStrOfEntB);
String decodedDesKey = decrypt(encryptedKey, privateKey);
System.out.println("解密后的DES Key: " + decodedDesKey);
}
// 将字符串转换为私钥对象
public static PrivateKey convertStringToPrivateKey(String privateKeyString) throws Exception {
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
return keyFactory.generatePrivate(keySpec);
}
// 使用RSA私钥解密字符串
public static String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}
解密后的DES Key:
lLkL6UwL+/uoy1RUhdPsV67NIP4a1eA7
用DES key对encrypted_data解密:
package encrypt;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class TripleDesDecrypt {
public static void main(String[] args) {
try {
// 密钥字符串
String desKey = "lLkL6UwL+/uoy1RUhdPsV67NIP4a1eA7";
// 加密的字符串
String encryptedData = "KiKj8YAQeyX6U9CP0xuJTA==";
// 将密钥字符串转换为密钥对象
SecretKey secretKey = convertStringToSecretKey(desKey);
// 解密字符串
String decryptedText = decrypt(encryptedData, secretKey);
System.out.println("解密后的报文: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
// 将密钥字符串转换为密钥对象
public static SecretKey convertStringToSecretKey(String keyString) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyString);
DESedeKeySpec keySpec = new DESedeKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
return keyFactory.generateSecret(keySpec);
}
// 使用3DES密钥解密字符串
public static String decrypt(String encryptedText, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}
解密后的报文:
Hello, World!
OK,现在我们还有最后一步:验签,即我要确认解密后的”Hello, World!”是否是企业A发的,且是否被劫持并修改过,验证过程:
package encrypt;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class RSASignatureExample {
public static void main(String[] args) {
try {
// 企业A的公钥
String publicKeyOfEntA = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtLmnzL7sqrv5LsUt2LYs5nEKdlt+BIgbTT4SYutPgCNFR83AE95w7/cQHCG1T79Agp3WbwpJOT6tCLUBQ48V2rRA235ZOtlswMKM1WE9TM6AuyXCGJLIFuK9pShCx7+6jksSNtuGxf2YfTFxKnP7VfjxEZKn26aCiSqzXBptxesVfPgHIB4OSRZFT360jBP/38ci37Q3vHND2z5plPMfdNRqgGawMb0lNsnCFi28l5jetpzQ25mfTUxaP0dT+m4+uXHm6vAwrYal0i/p0UWwHgzVZUvKirZfHURYzZ4BWC9grbSAA7XMLaxhdR/J3pEGT6rA0onrhQUjafWTsVpVYwIDAQAB";
PublicKey publicKey = convertStringToPublicKey(publicKeyOfEntA);
// 需要验签的字符串
String plaintext = "Hello, World!";
// 签名串
String signature = "W2vPVeQtfIGLSYajA9PZzHuC0W7KPhfOXdC+osyBLYpYEm40NGshnKd8S8t5XMq6Tf/ZJ96fHeyS6XOIVjZCE0WZcl5J7s8uvkdjKKir35Itbj3hYidTVO1B93KdoAJRtjUd0NxaWN+uscJB5VepkNqayS4+wwyOf9HRQkcoRjNTzkHupUZlQ9bTedjIm3NK5U6qONbwqlEvgdYLWOW1DmXBdZ3UKbgTrlEmRYYHcI9xth1nbJr9YumENttHL0vzDFid1V3J5JqWN15jVfGPrUhSgZWeG8dQj3mP6uMQ5l2KRVLU7Ditvjy7MmINXmtHvKoeKyC5bO89y2em0TFfig==";
// 验证签名
boolean isValid = verify(plaintext, signature, publicKey);
System.out.println("签名是否有效: " + isValid);
} catch (Exception e) {
e.printStackTrace();
}
}
// 验证签名
public static boolean verify(String data, String signature, PublicKey publicKey) throws Exception {
Signature verifySignature = Signature.getInstance("SHA256withRSA");
verifySignature.initVerify(publicKey);
verifySignature.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return verifySignature.verify(signatureBytes);
}
// 将字符串转换为公钥对象
public static PublicKey convertStringToPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
return keyFactory.generatePublic(keySpec);
}
}
输出:
签名是否有效: true
由此,整个过程结束。
总结
1、3 DES对称加密速度快,用来加密报文体
2、RSA适用于对密钥加密
3、企业B向企业A发消息,复用上述流程
4、加签验签过程,更安全的做法应该是加入一些双方约定的规则,比如对加签前的报文做一些变换,如字段排序、编码格式变换、加入特殊字符等方式。