使用ChaCha20加密和解密字符串

时间:2016-06-24 07:04:19

标签: android encryption bouncycastle

  

我想使用 chacha20

解密和加密字符串

BouncyCastleProvider正在使用chacha20技术。所以我把它包括在内。并尝试了代码,但无法工作。

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);
    }

}

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

enter image description here

编辑和更新代码

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;
    }
}
  

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

2 个答案:

答案 0 :(得分:2)

密码的输出由随机位组成(通常受到8位字节的实现限制)。随机字节可能包含任何字符集中的无效字符。 如果需要字符串,请将密文编码为base 64。

此外,您在解密时重新生成IV。加密/解密期间的IV应匹配。

答案 1 :(得分:1)

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

在AES-GCM(已认证块密码算法)上使用ChaCha20-Poly1305(基于流密码的已认证加密算法)的可能原因是:

    当CPU不提供专用AES指令时,Chali20-Poly1305几乎比AES快3倍。英特尔处理器提供AES-NI指令集[1] 与AES-GCM IV不同,Chali20-Poly1305不需要随机数是不可预测的/随机的。因此,可以避免运行伪随机数生成器的开销[2]
  1. ChaCha20不像AES [1]

    一样容易受到缓存冲突定时攻击。
    ctypedef Bogus: [...]

这是一个JUnit测试:

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);
  }
}
相关问题