在Java中生成和使用两个密钥进行加密和解密

Posted

技术标签:

【中文标题】在Java中生成和使用两个密钥进行加密和解密【英文标题】:Generating and using two keys for encryption and decryption in Java 【发布时间】:2015-03-05 10:34:35 【问题描述】:

我正在开发一个需要我使用两个键的 Java 应用程序 从不同的字符串生成用于加密和解密。一 字符串来自用户,其他是主密钥。我在网上看了 并找到了一些关于它的参考资料。我真的很想要一些 帮助了解如何实现这一点。我会展示我现在拥有的。

正如您从代码中看到的那样,我使用了其他 *** 帖子中的一些代码并对其进行了一些修改。我只是不知道如何从 2 个字符串生成 2 个密钥,以及从哪里可以获得用于解密的 SecretKey desKey。

代码:

public class Encryption 

public void doStuff() 

    String plaintext = "abc";

    SecretKey k1 = generateDESkey();
    SecretKey k2 = generateDESkey();


    String firstEncryption = desEncryption(plaintext, k1);
    String decryption = desDecryption(firstEncryption, k2);
    String secondEncryption = desEncryption(decryption, k1);

    System.out.println(firstEncryption);
    System.out.println(decryption);
    System.out.println(secondEncryption);


public static SecretKey generateDESkey() 
    KeyGenerator keyGen = null;
    try 
        keyGen = KeyGenerator.getInstance("DESede");
     catch (NoSuchAlgorithmException ex) 
        Logger.getLogger(Test.class
                .getName()).log(Level.SEVERE, null, ex);
    
    try 
        assert keyGen != null;
        keyGen.init(112); // key length 56
        return keyGen.generateKey();
     catch (NullPointerException ex)
        return null;
    



public static String desEncryption(String strToEncrypt, SecretKey desKey) 
    try 
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, desKey);
        return Base64.encode(cipher.doFinal(strToEncrypt.getBytes()));
     catch (NoSuchAlgorithmException | NoSuchPaddingException |
            IllegalBlockSizeException | BadPaddingException |
            InvalidKeyException ex) 
        Logger.getLogger(Test.class
                .getName()).log(Level.SEVERE, null, ex);
    
    return null;



public static String desDecryption(String strToDecrypt, SecretKey desKey) 
    try 
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        return new String(cipher.doFinal(Base64.decode(strToDecrypt)));

     catch (NoSuchAlgorithmException |  BadPaddingException | IllegalBlockSizeException
            | InvalidKeyException | NoSuchPaddingException ex) 
        Logger.getLogger(Test.class
                .getName()).log(Level.SEVERE, null, ex);
    
    return null;



如果对此有任何困惑或疑问。请告诉我。

【问题讨论】:

那么现在的代码有什么问题呢?有像 BadPaddingException 这样的异常吗? @ArtjomB。我将其更改为 DESede,但如何从字符串为 DESede 生成密钥?另外,我正在寻找如何使用 2 个密钥的组合解密? 另外,您似乎正在执行三重三重 DES。这是想要的吗? 请你编辑你的标题来描述你的问题,而不是你的目标。 @Duncan 老实说,我对密码学的实现不太了解,我想用纯字符串生成两个密钥,并将它们用于加密/解密。 【参考方案1】:

修改前的代码答案。

您正在尝试仅使用两个键而不是三个键来执行 DESede。

这通常可能有效,但不像您所写的那样。问题是填充。在第二步中,您尝试使用其他密钥解密密文,而不是使用加密的密钥,因此在 256 次中解密将失败超过 255 次,因为填充将是错误的(也因为您使用 Base64 编码没有必要)。

如果你真的想这样做,你将不得不在没有填充和没有 Base64 编码的情况下进行解密。好在未编码的密文已经是块大小的倍数,所以没有阻止你使用"DES/ECB/NoPadding"

public static void main(String[] args) 
    // First I would like to create keys by giving Strings
    SecretKey k1 = generateDESkey();
    SecretKey k2 = generateDESkey();

    // encryption
    byte[] firstEncryption = desEncryption("plaintext".getBytes("UTF-8"), k1, false);
    byte[] decryption = desDecryption(firstEncryption, k2, true);
    byte[] secondEncryption = desEncryption(decryption, k1, true);

    // decryption
    byte[] firstDecryption = desDecryption(secondEncryption, k1, true);
    byte[] encryption = desEncryption(firstDecryption, k2, true);
    byte[] secondDecryption = desDecryption(encryption, k1, false);

    System.out.println(new String(secondDecryption)); // plaintext


public static byte[] desEncryption(byte[] strToEncrypt, SecretKey desKey, boolean noPadding) 
    try 
        Cipher cipher = Cipher.getInstance(noPadding ? "DES/ECB/NoPadding" : "DES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, desKey);
        return cipher.doFinal(strToEncrypt);
     catch (Exception ex) 
        ex.printStackTrace();
    
    return null;


public static byte[] desDecryption(byte[] strToDecrypt, SecretKey desKey, boolean noPadding) 
    try 
        Cipher cipher = Cipher.getInstance(noPadding ? "DES/ECB/NoPadding" : "DES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        return cipher.doFinal(strToDecrypt);
     catch (Exception ex) 
        ex.printStackTrace();
    
    return null;

当通用密钥以这种方式构造时,这实际上是具有两个密钥的 DESede 的等效实现:

SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();

byte[] edeKeyBytes = new byte[24];
System.arraycopy(k1.getEncoded(), 0, edeKeyBytes, 0, 8);
System.arraycopy(k2.getEncoded(), 0, edeKeyBytes, 8, 8);
System.arraycopy(k1.getEncoded(), 0, edeKeyBytes, 16, 8);

edeKey = new SecretKeySpec(edeKeyBytes, "DESede");

Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, edeKey);

System.out.println(Base64.encode(cipher.doFinal("plaintext".getBytes("UTF-8"))));

DESede 使用三个密钥,我们将其称为 k1、k2 和 k3。所有这些都连接成一个单字节数组。在您的情况下,第二次使用 k1 代替 k3。

【讨论】:

感谢您的回答,这对您有很大帮助。你是如何从纯字符串生成密钥的,我在你的代码中没有看到。 我没有。如果字符串不是密钥材料,而是某种密码,那么您可以使用 PBKDF2 之类的东西从字符串中派生密钥。 我会检查它,但如果你有一些如何从字符串派生密钥的例子,请告诉我。 @ArtjomB。感谢您提到这个问题,它可以工作,但是如果我想将自定义用户密钥与 PBEWithMD5AndDES 实例一起使用,您知道该怎么做吗? @FirasAlMannaa 如果您将byte[] 转换为String,那么您需要无损地进行。例如,通过使用 Base64 对其进行编码。简单使用new String(byte[])构造函数会导致字节丢失,因为密文可能有任意字节值,但字符串编码只支持部分字节值作为可打印字符。

以上是关于在Java中生成和使用两个密钥进行加密和解密的主要内容,如果未能解决你的问题,请参考以下文章

js中常见的数据加密与解密的方法

java签名与验签

20什么是非对称加密?

Java使用AES加解密

Java使用非对称数据加密RSA加密解密

非对称加密和对称加密