0014 一种加减密通信方案

Posted on Sat, Jul 1, 2023 密码学 信息安全

概述

本文介绍了业界常用的RSA非对称加减密、3 DES对称加减密、加签验签方法。

业界典型的加密通信方式

在业界比较常见的做法是:对报文进行对称加密,对称加密的密钥,用对方的RSA公钥加密。为了报文的完整性,还需要对报文进行加签。

其中,RSA公私钥对的生成,是在企业内部自主完成。企业自主生成的RSA公钥,需要发送给报文接收方。公钥用来加密报文,私钥用来解密报文。比如企业A将公钥发给企业B,企业A保管自己的私钥,企业B用公钥对报文加密,发送给企业A,企业A用私钥进行解密。如果企业B向企业A发送消息,那么企业A向企业B提供公钥,因此我们总结为:

△ 非对称加密如RSA,生成时会有一把公钥,一把私钥,私钥自己保存,公钥发给对方 △ 公钥用于加密,私钥用于解密 △ A要发消息给B,需要B的公钥;B要发消息给A,需要A的公钥

实践案例

本案例中,我们使用到的算法:

具体流程如图:

在两个企业已经互发了RSA公钥的前提下,消息发送方(企业A)加密流程为:

消息接受方(企业B)解密流程为:

算法选型解惑

  1. 密钥长度:RSA的安全性取决于密钥的长度。较长的密钥长度提供更高的安全性,但也导致更慢的性能。常用的RSA密钥长度为2048位或4096位。
  2. 加密速度:RSA的加密速度比对称加密算法(如AES)慢得多。这是因为RSA算法涉及大数的运算,包括模幂运算和大素数计算,这些运算需要较长的时间。因此,对大量数据进行RSA加密可能会显著降低性能。
  3. 加密限制:RSA加密的数据长度有限制。加密的数据块不能超过密钥长度,且通常要小于密钥长度减去一些填充的字节数。对于较长的数据,通常使用对称加密算法加密数据本身,然后使用RSA加密对称密钥。

基于以上原因,我们不用RSA算法来直接加密报文体本身。

  1. 兼容性:3DES是DES算法的增强版本,它使用相同的加密算法结构,只是增加了迭代次数。这意味着3DES可以与使用DES加密的旧系统兼容,同时提供更高的安全性。
  2. 安全性:DES算法的密钥长度较短(56位),已被认为不够安全。3DES通过使用三个不同的密钥对数据进行三次加密,有效地扩展了密钥长度,提供更高的安全性。它提供的密钥空间更大(168位),使得暴力破解变得非常困难。
  3. 抗击穷举攻击:3DES的安全性基于DES算法的困难性。对于穷举攻击(exhaustive search attack),即尝试所有可能的密钥进行解密的攻击,3DES提供了更大的密钥空间,使得攻击者需要更多的时间和计算资源来尝试所有可能的密钥。
  4. 广泛应用:由于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的公私钥信息。

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

企业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、加签验签过程,更安全的做法应该是加入一些双方约定的规则,比如对加签前的报文做一些变换,如字段排序、编码格式变换、加入特殊字符等方式。