Encrypt and decrypt a string using ChaCha20

I want to decrypt and encrypt a string using chacha20

BouncyCastleProvider uses the chacha20 technique. So I turned it into a jar. and tried the code, but could not 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 that does not give me the correct result.

enter image description here

Change and update 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 only have to decrypt the problem. It shows an error that the key must be 128 or 256 bits. What am I doing wrong.

0
android encryption bouncycastle
Jun 24 '16 at 7:04
source share
2 answers

The encryption output consists of random bits (typically limited to 8-byte implementations). Random bytes probably contain invalid characters in any character set. If a string is required, encode the ciphertext to base 64.

In addition, you re-generate IV for decryption. IV during encryption / decryption must match.

+2
Jun 24 '16 at 18:25
source share

ChaCha20 - Java 11 is now supported. Here is an example program for encryption and decryption using ChaCha20-Poly1305.

Possible reasons for using ChaCha20-Poly1305 (which is an authenticated encryption algorithm based on a stream cipher) through AES-GCM (which is an authenticated block encryption algorithm):

  1. ChaCha20-Poly1305 is almost 3 times faster than AES when the processor does not provide special AES instructions. Intel processors provide the AES-NI instruction set [1]
  2. ChaCha20-Poly1305 does not need a one-time number to be unpredictable / random, unlike AES-GCM IV. Thus, it is possible to avoid the overhead of the operation of the pseudo random number generator [2].
  3. ChaCha20 is not vulnerable to cache collision synchronization 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 the 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; } } 
0
Jan 27 '19 at 8:21
source share



All Articles