使用 ChaCha20 加密和解密字符串 [英] Encrypt and decrypt string using ChaCha20

查看:79
本文介绍了使用 ChaCha20 加密和解密字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

<块引用>

我想使用 chacha20

解密和加密一个字符串

BouncyCastleProvider 正在使用 chacha20 技术.所以我把它包括在罐子里.并尝试了代码但无法工作.

PBE.java

公共类 PBE 扩展 AppCompatActivity {private static final String salt = "一个长但不变的短语,每次都将用作盐.";私有静态最终 int 迭代 = 2000;私有静态最终 int keyLength = 256;private static final SecureRandom random = new SecureRandom();@覆盖protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.pbe);尝试 {Security.insertProviderAt(new BouncyCastleProvider(), 1);//Security.addProvider(new BouncyCastleProvider());String passphrase = "敏捷的棕色狐狸跳过了懒惰的棕色狗";String 明文 = "你好";字节 [] 密文 = 加密(密码,明文);字符串恢复明文 = 解密(密码,密文);TextView encryptedTv = (TextView) findViewById(R.id.tv_decrypt);解密Tv.setText(恢复纯文本);System.out.println(recoveredPlaintext);}catch(异常e){e.printStackTrace();}}私有静态字节 [] 加密(字符串密码,字符串明文)抛出异常 {SecretKey 密钥 = generateKey(密码);Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING");//,new BouncyCastleProvider());cipher.init(Cipher.ENCRYPT_MODE, key, generateIV(cipher), random);返回 cipher.doFinal(plaintext.getBytes());}私有静态字符串解密(字符串密码,字节 [] 密文)抛出异常 {SecretKey 密钥 = generateKey(密码);Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING");//, new BouncyCastleProvider());cipher.init(Cipher.DECRYPT_MODE, key, generateIV(cipher), random);返回新字符串(cipher.doFinal(ciphertext));}私有静态 SecretKey generateKey(String passphrase) 抛出异常 {PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(), salt.getBytes(), iterations, keyLength);SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");返回 keyFactory.generateSecret(keySpec);}私有静态 IvParameterSpec generateIV(Cipher cipher) 抛出异常 {字节 [] ivBytes = 新字节 [cipher.getBlockSize()];random.nextBytes(ivBytes);返回新的 IvParameterSpec(ivBytes);}}

但它没有给我正确的结果..

编辑和更新代码

公共类 ChaCha20Encryptor 实现 Encryptor {私有最终字节 randomIvBytes[] = {0, 1, 2, 3, 4, 5, 6, 7};静止的 {Security.addProvider(new BouncyCastleProvider());}@覆盖public byte[] encrypt(byte[] data, byte[] randomKeyBytes) 抛出 IOException, InvalidKeyException,InvalidAlgorithmParameterException, InvalidCipherTextException {ChaChaEngine cipher = new ChaChaEngine();CipherParameters cp = new KeyParameter(getMyKey(randomKeyBytes));cipher.init(true, new ParametersWithIV(cp, randomIvBytes));//cipher.init(true, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));字节 [] 结果 = 新字节 [数据.长度];cipher.processBytes(data, 0, data.length, result, 0);返回结果;}@覆盖公共字节 [] 解密(字节 [] 数据,字节 [] randomKeyBytes)抛出 InvalidKeyException、InvalidAlgorithmParameterException、IOException、IllegalStateException, InvalidCipherTextException {ChaChaEngine cipher = new ChaChaEngine();CipherParameters cp = new KeyParameter(getMyKey(randomKeyBytes));cipher.init(false, new ParametersWithIV(cp, randomIvBytes));//cipher.init(false, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));字节 [] 结果 = 新字节 [数据.长度];cipher.processBytes(data, 0, data.length, result, 0);返回结果;}@覆盖公共 int getKeyLength() {返回 32;}@覆盖公共字符串 toString() {返回 "ChaCha20()";}私有静态字节[] getMyKey(字节[]键){尝试 {//byte[] key = encodekey.getBytes("UTF-8");MessageDigest sha = MessageDigest.getInstance("SHA-1");键 = sha.digest(key);key = Arrays.copyOf(key, 16);//只使用前 128 位}捕获(NoSuchAlgorithmException e){e.printStackTrace();}返回键;}}

<块引用>

现在我只有解密问题.它显示密钥必须是 128 或 256 位的错误.我究竟做错了什么.

解决方案

2019 年 12 月 24 日更新(更正)

与 AES 中的其他一些模式(如 CBC)不同,GCM 模式不需要 IV 不可预测(与 Chacha20-Poly1305 相同).唯一的要求是对于使用给定密钥的每次调用,IV(对于 AES)或随机数(对于 Chacha20-Poly1305)必须是唯一的.如果对给定的密钥重复一次,则安全性可能会受到影响.实现这一点的一种简单方法是使用来自强伪随机数生成器的随机 IV 或 nonce,如下所示.IV 或 nonce 碰撞的概率(假设一个强随机源)最多为 2^-32,这足以阻止攻击者.

使用序列或时间戳作为 IV 或随机数也是可能的,但这可能不像听起来那么简单.例如,如果系统没有正确跟踪已在持久存储中用作 IV 的序列,则调用可能会在系统重新启动后重复执行 IV.同样,没有完美的时钟.电脑时钟调整等

此外,应在每 2^32 次调用后轮换密钥.

SecureRandom.getInstanceStrong() 可用于生成加密强随机随机数.


原答案

现在支持ChaCha20的是Java 11.这里是一个使用ChaCha20-Poly1305进行加密和解密的示例程序.

使用 ChaCha20-Poly1305(一种基于流密码的认证加密算法)而不是 AES-GCM(一种认证分组密码算法)的可能原因是:

  1. 当 CPU 不提供专用 AES 指令时,ChaCha20-Poly1305 几乎比 AES 快 3 倍.Intel 处理器提供 AES-NI 指令集 [1]

  2. ChaCha20-Poly1305 不像 AES-GCM 的 IV 那样需要 nonce 是不可预测的/随机的.因此可以避免运行伪随机数生成器的开销[2]

  3. 与 AES 不同,ChaCha20 不易受到缓存冲突计时攻击的影响 [1]

    package com.sapbasu.javastudy;导入 java.lang.reflect.Field;导入 java.math.BigInteger;导入 java.util.Arrays;导入 java.util.Objects;导入 javax.crypto.Cipher;导入 javax.crypto.spec.IvParameterSpec;导入 javax.crypto.spec.SecretKeySpec;导入 javax.security.auth.Destroyable;/**** 使用 ChaCha20-Poly1305 的可能原因* 基于流密码的认证加密算法* 1.如果CPU没有提供专用的AES指令,* ChaCha20 比 AES 更快* 2. ChaCha20 不易受到缓存冲突时间的影响* 攻击与 AES 不同* 3. 由于 nonce 不需要是随机的.有* 没有生成加密安全的开销* 伪随机数**/公共类 CryptoChaCha20 {private static final String ENCRYPT_ALGO = "ChaCha20-Poly1305/None/NoPadding";私有静态最终 int KEY_LEN = 256;私有静态最终 int NONCE_LEN = 12;//字节private static final BigInteger NONCE_MIN_VAL = new BigInteger(100000000000000000000000", 16);private static final BigInteger NONCE_MAX_VAL = new BigInteger("ffffffffffffffffffffffff", 16);私有静态 BigInteger nonceCounter = NONCE_MIN_VAL;公共静态字节 [] 加密(字节 [] 输入,SecretKeySpec 密钥)抛出异常{Objects.requireNonNull(input, 输入消息不能为空");Objects.requireNonNull(key, key 不能为 null");如果(输入.长度== 0){throw new IllegalArgumentException(消息长度不能为0");}if (key.getEncoded().length * 8 != KEY_LEN) {throw new IllegalArgumentException(密钥大小必须为 256 位");}密码密码 = Cipher.getInstance(ENCRYPT_ALGO);byte[] nonce = getNonce();IvParameterSpec ivParameterSpec = new IvParameterSpec(nonce);cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);byte[] messageCipher = cipher.doFinal(input);//在随机数前加上消息密码byte[] cipherText = new byte[messageCipher.length + NONCE_LEN];System.arraycopy(nonce, 0, cipherText, 0, NONCE_LEN);System.arraycopy(messageCipher, 0, cipherText, NONCE_LEN,messageCipher.length);返回密文;}公共静态字节 [] 解密(字节 [] 输入,SecretKeySpec 密钥)抛出异常{Objects.requireNonNull(input, 输入消息不能为空");Objects.requireNonNull(key, key 不能为 null");如果(输入.长度== 0){throw new IllegalArgumentException(输入数组不能为空");}字节 [] 随机数 = 新字节 [NONCE_LEN];System.arraycopy(input, 0, nonce, 0, NONCE_LEN);byte[] messageCipher = new byte[input.length - NONCE_LEN];System.arraycopy(input, NONCE_LEN, messageCipher, 0, input.length - NONCE_LEN);IvParameterSpec ivParameterSpec = new IvParameterSpec(nonce);密码密码 = Cipher.getInstance(ENCRYPT_ALGO);cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);返回 cipher.doFinal(messageCipher);}/**** 此方法创建 96 位随机数.一个 96 位的随机数* 对于 ChaCha20-Poly1305 是必需的.随机数不是* 一个秘密.唯一的要求是它必须是* 对于给定的键是唯一的.下面的函数实现* 一个 96 位计数器,调用时总是递增* 计数器一一.** @返回*/公共静态字节[] getNonce() {if (nonceCounter.compareTo(NONCE_MAX_VAL) == -1) {return nonceCounter.add(BigInteger.ONE).toByteArray();} 别的 {nonceCounter = NONCE_MIN_VAL;返回 NONCE_MIN_VAL.toByteArray();}}/**** 不应使用字符串来保存明文消息或密钥,因为* 字符串进入字符串池,它们将出现在堆转储中.为了* 同理,客户端调用这些加密或解密方法* 应该清除所有保存消息或密钥的变量或数组* 在不再需要它们之后.由于 Java 8 没有提供简单的* 从 {@code SecretKeySpec} 清除密钥的机制,该方法使用* 反射清除键** @param 键* 用于加密的密钥* @throws IllegalArgumentException* @throws IllegalAccessException* @throws NoSuchFieldException* @throws 安全异常*/@SuppressWarnings(未使用")public static void clearSecret(可销毁的密钥)抛出 IllegalArgumentException、IllegalAccessException、NoSuchFieldException, SecurityException {字段 keyField = key.getClass().getDeclaredField("key");keyField.setAccessible(true);字节 [] 编码密钥 = (字节 []) keyField.get(key);Arrays.fill(encodedKey, Byte.MIN_VALUE);}}

而且,这是一个 JUnit 测试:

package com.sapbasu.javastudy;导入静态 org.junit.jupiter.api.Assertions.assertArrayEquals;导入 java.nio.ByteBuffer;导入 java.nio.CharBuffer;导入 java.nio.charset.Charset;导入 java.security.SecureRandom;导入 javax.crypto.KeyGenerator;导入 javax.crypto.SecretKey;导入 javax.crypto.spec.SecretKeySpec;导入 org.junit.jupiter.api.Test;公共类 CryptoChaCha20Test {私人整数 KEY_LEN = 256;//位@测试public void whenDecryptCalled_givenEncryptedTest_returnsDecryptedBytes()抛出异常{char[] input = {'e', 'n', 'c', 'r', 'y', 'p', 't', 'i', 'o', 'n'};byte[] inputBytes = convertInputToBytes(input);KeyGenerator keyGen = KeyGenerator.getInstance(ChaCha20");keyGen.init(KEY_LEN, SecureRandom.getInstanceStrong());SecretKey secretKey = keyGen.generateKey();SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),ChaCha20");CryptoChaCha20.clearSecret(secretKey);byte[] encryptedBytes = CryptoChaCha20.encrypt(inputBytes, secretKeySpec);字节 [] 解密字节 = CryptoChaCha20.decrypt(encryptedBytes, secretKeySpec);CryptoChaCha20.clearSecret(secretKeySpec);assertArrayEquals(inputBytes,decryptedBytes);}私有字节[] convertInputToBytes(char[] input) {CharBuffer charBuf = CharBuffer.wrap(input);ByteBuffer byteBuf = Charset.forName(Charset.defaultCharset().name()).encode(charBuf);byte[] inputBytes = byteBuf.array();charBuf.clear();byteBuf.clear();返回输入字节;}}

I want to decrypt and encrypt a string using chacha20

BouncyCastleProvider is using chacha20 technique. So I included it jar. and tried the code but not able to work.

PBE.java

public class PBE extends AppCompatActivity {

    private static final String salt = "A long, but constant phrase that will be used each time as the salt.";
    private static final int iterations = 2000;
    private static final int keyLength = 256;
    private static final SecureRandom random = new SecureRandom();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pbe);

        try {
            Security.insertProviderAt(new BouncyCastleProvider(), 1);
            //Security.addProvider(new BouncyCastleProvider());

            String passphrase = "The quick brown fox jumped over the lazy brown dog";
            String plaintext = "Hello";
            byte [] ciphertext = encrypt(passphrase, plaintext);
            String recoveredPlaintext = decrypt(passphrase, ciphertext);

            TextView decryptedTv = (TextView) findViewById(R.id.tv_decrypt);

            decryptedTv.setText(recoveredPlaintext);

            System.out.println(recoveredPlaintext);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private static byte [] encrypt(String passphrase, String plaintext) throws Exception {
        SecretKey key = generateKey(passphrase);

        Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING");//,new BouncyCastleProvider());
        cipher.init(Cipher.ENCRYPT_MODE, key, generateIV(cipher), random);
        return cipher.doFinal(plaintext.getBytes());
    }

    private static String decrypt(String passphrase, byte [] ciphertext) throws Exception {
        SecretKey key = generateKey(passphrase);

        Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING");// , new BouncyCastleProvider());
        cipher.init(Cipher.DECRYPT_MODE, key, generateIV(cipher), random);
        return new String(cipher.doFinal(ciphertext));
    }

    private static SecretKey generateKey(String passphrase) throws Exception {
        PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(), salt.getBytes(), iterations, keyLength);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
        return keyFactory.generateSecret(keySpec);
    }

    private static IvParameterSpec generateIV(Cipher cipher) throws Exception {
        byte [] ivBytes = new byte[cipher.getBlockSize()];
        random.nextBytes(ivBytes);
        return new IvParameterSpec(ivBytes);
    }

}

But it is not giving me proper result..

Edit and Updated Code

public class ChaCha20Encryptor implements Encryptor {

    private final byte randomIvBytes[] = {0, 1, 2, 3, 4, 5, 6, 7};

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    @Override
    public byte[] encrypt(byte[] data, byte[] randomKeyBytes) throws IOException, InvalidKeyException,
            InvalidAlgorithmParameterException, InvalidCipherTextException {

        ChaChaEngine cipher = new ChaChaEngine();
        CipherParameters cp = new KeyParameter(getMyKey(randomKeyBytes));
        cipher.init(true, new ParametersWithIV(cp , randomIvBytes));
        //cipher.init(true, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));

        byte[] result = new byte[data.length];
        cipher.processBytes(data, 0, data.length, result, 0);
        return result;
    }

    @Override
    public byte[] decrypt(byte[] data, byte[] randomKeyBytes)
            throws InvalidKeyException, InvalidAlgorithmParameterException, IOException,
            IllegalStateException, InvalidCipherTextException {

        ChaChaEngine cipher = new ChaChaEngine();
        CipherParameters cp = new KeyParameter(getMyKey(randomKeyBytes));
        cipher.init(false, new ParametersWithIV(cp , randomIvBytes));
        //cipher.init(false, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));

        byte[] result = new byte[data.length];
        cipher.processBytes(data, 0, data.length, result, 0);
        return result;
    }

    @Override
    public int getKeyLength() {
        return 32;
    }

    @Override
    public String toString() {
        return "ChaCha20()";
    }

    private static byte[] getMyKey(byte[] key){
        try {
            //byte[] key = encodekey.getBytes("UTF-8");
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); // use only first 128 bit
        }
        catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        return key;
    }
}

Now I have only problem decrypting. It shows an error that key must be 128 or 256 bits. What am I doing wrong.

解决方案

Update on 24-DEC-2019 (Correction)

Unlike some other modes in AES like CBC, GCM mode does not require the IV to be unpredictable (same as Chacha20-Poly1305). The only requirement is that the IV (for AES) or nonce (for Chacha20-Poly1305) has to be unique for each invocation with a given key. If it repeats once for a given key, security can be compromised. An easy way to achieve this with good probability is to use a random IV or nonce from a strong pseudo random number generator as shown below. Probability of an IV or nonce collision (assuming a strong random source) will be at most 2^-32, which is low enough to deter attackers.

Using a sequence or timestamp as IV or nonce is also possible, but it may not be as trivial as it may sound. For example, if the system does not correctly keep track of the sequences already used as IV in a persistent store, an invocation may repeat an IV after a system reboot. Likewise, there is no perfect clock. Computer clocks readjusts etc.

Also, the key should be rotated after every 2^32 invocations.

SecureRandom.getInstanceStrong() can be used to generate a cryptographically strong random nonce.


Original Answer

Now that ChaCha20 is supported is Java 11. Here is a sample program for encrypting and decrypting using ChaCha20-Poly1305.

The possible reasons for using ChaCha20-Poly1305 (which is a stream cipher based authenticated encryption algorithm) over AES-GCM (which is an authenticated block cipher algorithm) are:

  1. ChaCha20-Poly1305 is almost 3 times faster than AES when the CPU does not provide dedicated AES instructions. Intel processors provide AES-NI instruction set [1]

  2. ChaCha20-Poly1305 does not need the nonce to be unpredictable / random unlike the IV of AES-GCM. Thus the overhead for running pseudo random number generator can be avoided [2]

  3. ChaCha20 is not vulnerable to cache-collision timing attacks unlike AES [1]

    package com.sapbasu.javastudy;
    
    import java.lang.reflect.Field;
    import java.math.BigInteger;
    import java.util.Arrays;
    import java.util.Objects;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import javax.security.auth.Destroyable;
    
    /**
     * 
     * The possible reasons for using ChaCha20-Poly1305 which is a
     * stream cipher based authenticated encryption algorithm
     * 1. If the CPU does not provide dedicated AES instructions,
     *    ChaCha20 is faster than AES
     * 2. ChaCha20 is not vulnerable to cache-collision timing 
     *    attacks unlike AES
     * 3. Since the nonce is not required to be random. There is
     *    no overhead for generating cryptographically secured
     *    pseudo random number
     *
     */
    
    public class CryptoChaCha20 {
    
      private static final String ENCRYPT_ALGO = "ChaCha20-Poly1305/None/NoPadding";
    
      private static final int KEY_LEN = 256;
    
      private static final int NONCE_LEN = 12; //bytes
    
      private static final BigInteger NONCE_MIN_VAL = new BigInteger("100000000000000000000000", 16);
      private static final BigInteger NONCE_MAX_VAL = new BigInteger("ffffffffffffffffffffffff", 16);
    
      private static BigInteger nonceCounter = NONCE_MIN_VAL;
    
      public static byte[] encrypt(byte[] input, SecretKeySpec key)
          throws Exception {
        Objects.requireNonNull(input, "Input message cannot be null");
        Objects.requireNonNull(key, "key cannot be null");
    
        if (input.length == 0) {
          throw new IllegalArgumentException("Length of message cannot be 0");
        }
    
        if (key.getEncoded().length * 8 != KEY_LEN) {
          throw new IllegalArgumentException("Size of key must be 256 bits");
        }
    
        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
    
        byte[] nonce = getNonce();
    
        IvParameterSpec ivParameterSpec = new IvParameterSpec(nonce);
    
        cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
    
        byte[] messageCipher = cipher.doFinal(input);
    
        // Prepend the nonce with the message cipher
        byte[] cipherText = new byte[messageCipher.length + NONCE_LEN];
        System.arraycopy(nonce, 0, cipherText, 0, NONCE_LEN);
        System.arraycopy(messageCipher, 0, cipherText, NONCE_LEN,
            messageCipher.length);
        return cipherText;
      }
    
      public static byte[] decrypt(byte[] input, SecretKeySpec key)
          throws Exception {
        Objects.requireNonNull(input, "Input message cannot be null");
        Objects.requireNonNull(key, "key cannot be null");
    
        if (input.length == 0) {
          throw new IllegalArgumentException("Input array cannot be empty");
        }
    
        byte[] nonce = new byte[NONCE_LEN];
        System.arraycopy(input, 0, nonce, 0, NONCE_LEN);
    
        byte[] messageCipher = new byte[input.length - NONCE_LEN];
        System.arraycopy(input, NONCE_LEN, messageCipher, 0, input.length - NONCE_LEN);
    
        IvParameterSpec ivParameterSpec = new IvParameterSpec(nonce);
    
        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
    
        return cipher.doFinal(messageCipher);
      }
    
    
      /**
       * 
       * This method creates the 96 bit nonce. A 96 bit nonce 
       * is required for ChaCha20-Poly1305. The nonce is not 
       * a secret. The only requirement being it has to be 
       * unique for a given key. The following function implements 
       * a 96 bit counter which when invoked always increments 
       * the counter by one.
       * 
       * @return
       */
      public static byte[] getNonce() {
        if (nonceCounter.compareTo(NONCE_MAX_VAL) == -1) {
          return nonceCounter.add(BigInteger.ONE).toByteArray();
        } else {
          nonceCounter = NONCE_MIN_VAL;
          return NONCE_MIN_VAL.toByteArray();
        }
      }
      /**
       * 
       * Strings should not be used to hold the clear text message or the key, as
       * Strings go in the String pool and they will show up in a heap dump. For the
       * same reason, the client calling these encryption or decryption methods
       * should clear all the variables or arrays holding the message or the key
       * after they are no longer needed. Since Java 8 does not provide an easy
       * mechanism to clear the key from {@code SecretKeySpec}, this method uses
       * reflection to clear the key
       * 
       * @param key
       *          The secret key used to do the encryption
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws NoSuchFieldException
       * @throws SecurityException
       */
      @SuppressWarnings("unused")
      public static void clearSecret(Destroyable key)
          throws IllegalArgumentException, IllegalAccessException,
          NoSuchFieldException, SecurityException {
        Field keyField = key.getClass().getDeclaredField("key");
        keyField.setAccessible(true);
        byte[] encodedKey = (byte[]) keyField.get(key);
        Arrays.fill(encodedKey, Byte.MIN_VALUE);
      }
    }
    

And, here is a JUnit test:

package com.sapbasu.javastudy;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.SecureRandom;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.junit.jupiter.api.Test;

public class CryptoChaCha20Test {
  
  private int KEY_LEN = 256; // bits
  
  @Test
  public void whenDecryptCalled_givenEncryptedTest_returnsDecryptedBytes()
      throws Exception {
    
    char[] input = {'e', 'n', 'c', 'r', 'y', 'p', 't', 'i', 'o', 'n'};
    byte[] inputBytes = convertInputToBytes(input);
    
    KeyGenerator keyGen = KeyGenerator.getInstance("ChaCha20");
    keyGen.init(KEY_LEN, SecureRandom.getInstanceStrong());
    SecretKey secretKey = keyGen.generateKey();
    
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),
        "ChaCha20");
    CryptoChaCha20.clearSecret(secretKey);
    
    byte[] encryptedBytes = CryptoChaCha20.encrypt(inputBytes, secretKeySpec);
    byte[] decryptedBytes = CryptoChaCha20.decrypt(encryptedBytes, secretKeySpec);
    
    CryptoChaCha20.clearSecret(secretKeySpec);
    
    assertArrayEquals(inputBytes, decryptedBytes);
    
  }
  
  private byte[] convertInputToBytes(char[] input) {
    CharBuffer charBuf = CharBuffer.wrap(input);
    ByteBuffer byteBuf = Charset.forName(Charset.defaultCharset().name())
        .encode(charBuf);
    byte[] inputBytes = byteBuf.array();
    charBuf.clear();
    byteBuf.clear();
    return inputBytes;
  }
}

这篇关于使用 ChaCha20 加密和解密字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆