我想使用 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);
}
}
但它没有给我正确的结果..
编辑和更新代码
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位的错误。我究竟做错了什么。
答案 0 :(得分:2)
密码的输出由随机位组成(通常受到8位字节的实现限制)。随机字节可能包含任何字符集中的无效字符。 如果需要字符串,请将密文编码为base 64。
此外,您在解密时重新生成IV。加密/解密期间的IV应匹配。
答案 1 :(得分:1)
现在支持ChaCha20的是Java11。这是一个使用ChaCha20-Poly1305进行加密和解密的示例程序。
在AES-GCM(已认证块密码算法)上使用ChaCha20-Poly1305(基于流密码的已认证加密算法)的可能原因是:
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);
}
}