Java 11 – ChaCha20加密示例

在本文中,我们将向您展示如何使用RFC 7539中定义的ChaCha20流密码对消息进行加密和解密。

PS ChaCha20流密码可从Java 11获得,请参阅JEP 329

注意
您可能对此ChaCha20-Poly1305加密示例感兴趣

1.如何运作?

1.1 ChaCha20加密和解密的输入:

  • 256位密钥(32字节)
  • 96位随机数(12个字节)
  • 32位初始计数(4个字节)

1.2 ChaCha20加密。

(plain text) + (secrect key | nonce | initial count) -> `ChaCha20` -> ciphertext (encrypted text).

1.2 ChaCha20解密。

ciphertext + (secrect key | nonce | initial count) -> `ChaCha20` -> plain text.

1.3 ChaCha20加密使用密钥和IV(初始化值,随机数+初始计数)将纯文本加密为等长的密文。

1.4每次加密的随机数和秘密密钥必须唯一。 随机数和初始计数可以公开知道,但是密钥必须是私有的并对其保密。

ChaCha20 Java实现
下载JDK源代码,并找到用于ChaCha20算法实现的此类ChaCha20Cipher

ChaCha20Cipher.java
package com.sun.crypto.provider;

/**
 * Implementation of the ChaCha20 cipher, as described in RFC 7539.
 *
 * @since 11
 */
abstract class ChaCha20Cipher extends CipherSpi {
  //...
}

2. ChaCha20加密和解密。

2.1一个使用ChaCha20算法加密和解密消息的Java示例。

ChaCha20.java
package com.mkyong.java11.jep329.chacha20;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.ChaCha20ParameterSpec;

/*
The inputs to ChaCha20 encryption, specified by RFC 7539, are:
 - A 256-bit secret key (32 bytes)
 - A 96-bit nonce (12 bytes)
 - A 32-bit initial count (4 bytes)
*/
public class ChaCha20 {

    private static final String ENCRYPT_ALGO = "ChaCha20";

    public byte[] encrypt(byte[] pText, SecretKey key, byte[] nonce, int counter) throws Exception {

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

        ChaCha20ParameterSpec param = new ChaCha20ParameterSpec(nonce, counter);

        cipher.init(Cipher.ENCRYPT_MODE, key, param);

        byte[] encryptedText = cipher.doFinal(pText);

        return encryptedText;
    }

    public byte[] decrypt(byte[] cText, SecretKey key, byte[] nonce, int counter) throws Exception {

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

        ChaCha20ParameterSpec param = new ChaCha20ParameterSpec(nonce, counter);

        cipher.init(Cipher.DECRYPT_MODE, key, param);

        byte[] decryptedText = cipher.doFinal(cText);

        return decryptedText;

    }

}

2.1测试。

TestChaCha20.java
package com.mkyong.java11.jep329.chacha20;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class TestChaCha20 {

    public static void main(String[] args) throws Exception {

        String input = "Java & ChaCha20 encryption example.";

        ChaCha20 cipher = new ChaCha20();

        SecretKey key = getKey();           // 256-bit secret key (32 bytes)
        byte[] nonce = getNonce();          // 96-bit nonce (12 bytes)
        int counter = 1;                    // 32-bit initial count (8 bytes)

        System.out.println("Input          : " + input);
        System.out.println("Input     (hex): " + convertBytesToHex(input.getBytes()));

        System.out.println("\n---Encryption---");
        byte[] cText = cipher.encrypt(input.getBytes(), key, nonce, counter);   // encrypt

        System.out.println("Key       (hex): " + convertBytesToHex(key.getEncoded()));
        System.out.println("Nonce     (hex): " + convertBytesToHex(nonce));
        System.out.println("Counter        : " + counter);
        System.out.println("Encrypted (hex): " + convertBytesToHex(cText));

        System.out.println("\n---Decryption---");

        byte[] pText = cipher.decrypt(cText, key, nonce, counter);              // decrypt

        System.out.println("Key       (hex): " + convertBytesToHex(key.getEncoded()));
        System.out.println("Nonce     (hex): " + convertBytesToHex(nonce));
        System.out.println("Counter        : " + counter);
        System.out.println("Decrypted (hex): " + convertBytesToHex(pText));
        System.out.println("Decrypted      : " + new String(pText));

    }

    // A 256-bit secret key (32 bytes)
    private static SecretKey getKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGen = KeyGenerator.getInstance("ChaCha20");
        keyGen.init(256, SecureRandom.getInstanceStrong());
        return keyGen.generateKey();
    }

    // 96-bit nonce (12 bytes)
    private static byte[] getNonce() {
        byte[] newNonce = new byte[12];
        new SecureRandom().nextBytes(newNonce);
        return newNonce;
    }

    private static String convertBytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte temp : bytes) {
            result.append(String.format("%02x", temp));
        }
        return result.toString();
    }

}

输出量

Terminal
Input          : Java & ChaCha20 encryption example.
Input     (hex): 4a617661202620436861436861323020656e6372797074696f6e206578616d706c652e

---Encryption---
Key       (hex): ee416df8b5154a4ac48f3930fcfa53ef7f677c8fd7cd093f1328eedfd831db1a
Nonce     (hex): 9806308f4d1732d2d39beaba
Counter        : 1
Encrypted (hex): 2149db2c32bf82f9e8dc0a709d8c15d5a22eb79d5f692e04f070d46cc7e264631f85e0

---Decryption---
Key       (hex): ee416df8b5154a4ac48f3930fcfa53ef7f677c8fd7cd093f1328eedfd831db1a
Nonce     (hex): 9806308f4d1732d2d39beaba
Counter        : 1
Decrypted (hex): 4a617661202620436861436861323020656e6372797074696f6e206578616d706c652e
Decrypted      : Java & ChaCha20 encryption example.

key + nonce + counter必须相同,才能进行加密和解密,不同的单个位将产生不同的结果。

3. ChaCha20…版本2

3.1在此示例中,我们使用System.arraycopy将现时数和初始化计数器附加到密文(加密消息)的末尾,因此我们只需要用于解密的密钥即可。

ChaCha20v2.java
package com.mkyong.java11.jep329.chacha20v2;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.ChaCha20ParameterSpec;
import java.nio.ByteBuffer;

public class ChaCha20v2 {

    private static final String ENCRYPT_ALGO = "ChaCha20";

    private static final int LEN_NONCE = 12;
    private static final int LEN_COUNTER = 4;

    public byte[] encrypt(byte[] pText, SecretKey key, byte[] nonce, int counter) throws Exception {

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

        ChaCha20ParameterSpec param = new ChaCha20ParameterSpec(nonce, counter);

        cipher.init(Cipher.ENCRYPT_MODE, key, param);

        byte[] encryptedText = cipher.doFinal(pText);

        // append nonce + count
        byte[] output = new byte[encryptedText.length + LEN_NONCE + LEN_COUNTER];

        System.arraycopy(encryptedText, 0, output, 0, encryptedText.length);
        System.arraycopy(nonce, 0, output, encryptedText.length, LEN_NONCE);

        // convert int to byte[]
        byte[] counterByteArray = ByteBuffer.allocate(4).putInt(counter).array();
        System.arraycopy(counterByteArray, 0, output, encryptedText.length + LEN_NONCE, LEN_COUNTER);

        return output;
    }

    public byte[] decrypt(byte[] cText, SecretKey key) throws Exception {

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

        // get only the encrypted text
        byte[] encryptedText = new byte[cText.length - (LEN_NONCE + LEN_COUNTER)];
        System.arraycopy(cText, 0, encryptedText, 0, cText.length - (LEN_NONCE + LEN_COUNTER));

        // get nonce
        byte[] nonce = new byte[12];
        System.arraycopy(cText, encryptedText.length, nonce, 0, LEN_NONCE);

        // get initial counter
        byte[] counter = new byte[4];
        System.arraycopy(cText, encryptedText.length + LEN_NONCE, counter, 0, LEN_COUNTER);

        /// convert byte array to int
        int ic = ByteBuffer.wrap(counter).getInt();
        ChaCha20ParameterSpec param = new ChaCha20ParameterSpec(nonce, ic);

        cipher.init(Cipher.DECRYPT_MODE, key, param);

        byte[] decryptedText = cipher.doFinal(encryptedText);

        return decryptedText;

    }

}

3.2对于解密,我们只需要提供秘密密钥,因为随机数和初始计数器位于加密文本或密文的末尾。

TestChaCha20v2.java
package com.mkyong.java11.jep329.chacha20v2;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class TestChaCha20v2 {

    public static void main(String[] args) throws Exception {

        String input = "Java & ChaCha20 encryption example.";

        ChaCha20v2 cipher = new ChaCha20v2();

        SecretKey key = getKey();           // 256-bit secret key (32 bytes)
        byte[] nonce = getNonce();          // 96-bit nonce (12 bytes)
        int counter = 1;                    // 32-bit initial count (8 bytes)

        System.out.println("Input          : " + input);
        System.out.println("Input     (hex): " + convertBytesToHex(input.getBytes()));

        System.out.println("\n---Encryption---");
        byte[] cText = cipher.encrypt(input.getBytes(), key, nonce, counter);   // encrypt

        System.out.println("Key       (hex): " + convertBytesToHex(key.getEncoded()));
        System.out.println("Nonce     (hex): " + convertBytesToHex(nonce));
        System.out.println("Counter        : " + counter);
        System.out.println("Encrypted (hex): " + convertBytesToHex(cText));

        System.out.println("\n---Decryption---");

        byte[] pText = cipher.decrypt(cText, key);              // decrypt

        System.out.println("Key       (hex): " + convertBytesToHex(key.getEncoded()));
        System.out.println("Decrypted (hex): " + convertBytesToHex(pText));
        System.out.println("Decrypted      : " + new String(pText));

    }

    private static String convertBytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte temp : bytes) {
            result.append(String.format("%02x", temp));
        }
        return result.toString();
    }

    // A 256-bit secret key (32 bytes)
    private static SecretKey getKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGen = KeyGenerator.getInstance("ChaCha20");
        keyGen.init(256, SecureRandom.getInstanceStrong());
        return keyGen.generateKey();
    }

    // 96-bit nonce (12 bytes)
    private static byte[] getNonce() {
        byte[] newNonce = new byte[12];
        new SecureRandom().nextBytes(newNonce);
        return newNonce;
    }

}

输出量

Terminal
Input          : Java & ChaCha20 encryption example.
Input     (hex): 4a617661202620436861436861323020656e6372797074696f6e206578616d706c652e

---Encryption---
Key       (hex): f95fd5b41783595e41f4cbcd8dc26a782599184e97ccd768ac531aae729781d3
Nonce     (hex): 84133f2261ef44796e3669dc
Counter        : 1
Encrypted (hex): 7738807b409f3349dbefbeae988482e0e5959c35ee8f8ee8987357db459e10d7fb8c7e84133f2261ef44796e3669dc00000001

---Decryption---
Key       (hex): f95fd5b41783595e41f4cbcd8dc26a782599184e97ccd768ac531aae729781d3
Decrypted (hex): 4a617661202620436861436861323020656e6372797074696f6e206578616d706c652e
Decrypted      : Java & ChaCha20 encryption example.

查看加密的文本; 它结合了加密的消息,随机数(12个字节)和初始计数器(4个字节)。

Encrypted (hex): 7738807b409f3349dbefbeae988482e0e5959c35ee8f8ee8987357db459e10d7fb8c7e84133f2261ef44796e3669dc00000001

## split view
Encrypted (hex): 7738807b409f3349dbefbeae988482e0e5959c35ee8f8ee8987357db459e10d7fb8c7e | 84133f2261ef44796e3669dc | 00000001

注意
如果发现任何错误,请在下面发表评论,谢谢。

下载源代码

$ git clone https://github.com/mkyong/core-java.git

$ cd java-11

$ cd / src / main / java / com / mkyong / java11 / jep329

参考文献

翻译自: https://mkyong.com/java/java-11-chacha20-stream-cipher-examples/

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值