问题
If I have a BlockCipher
and a byte[]
that I got from a String
containing a secret message, what's the easiest way to get a byte[]
of the message encrypted?
In the normal Java API, I could just do cipher.doFinal(secretMessage)
, but there doesn't seem to be anything like that here, it only processes blocks.
I know I can use a BufferedBlockCipher
, but this still doesn't simplify things significantly. What's the easiest high-level way to use this cipher?
回答1:
OK, so using the lightweight API and counter mode, which is one of the easiest and modern modes you would get:
public class BouncyEncrypt {
private static final int IV_SIZE = 16;
public static void main(String[] args) throws Exception {
// key should really consist of 16 random bytes
byte[] keyData = new byte[256 / Byte.SIZE];
KeyParameter key = new KeyParameter(keyData);
byte[] ciphertext = encryptWithAES_CTR(key, "owlstead");
System.out.println(decryptWithAES_CTR(key, ciphertext));
}
private static byte[] encryptWithAES_CTR(KeyParameter key, String in)
throws IllegalArgumentException, UnsupportedEncodingException,
DataLengthException {
// iv should be unique for each encryption with the same key
byte[] ivData = new byte[IV_SIZE];
SecureRandom rng = new SecureRandom();
rng.nextBytes(ivData);
ParametersWithIV iv = new ParametersWithIV(key, ivData);
SICBlockCipher aesCTR = new SICBlockCipher(new AESFastEngine());
aesCTR.init(true, iv);
byte[] plaintext = in.getBytes("UTF-8");
byte[] ciphertext = new byte[ivData.length + plaintext.length];
System.arraycopy(ivData, 0, ciphertext, 0, IV_SIZE);
aesCTR.processBytes(plaintext, 0, plaintext.length, ciphertext, IV_SIZE);
return ciphertext;
}
private static String decryptWithAES_CTR(KeyParameter key, byte[] ciphertext)
throws IllegalArgumentException, UnsupportedEncodingException,
DataLengthException {
if (ciphertext.length < IV_SIZE) {
throw new IllegalArgumentException("Ciphertext too short to contain IV");
}
ParametersWithIV iv = new ParametersWithIV(key, ciphertext, 0, IV_SIZE);
SICBlockCipher aesCTR = new SICBlockCipher(new AESFastEngine());
aesCTR.init(true, iv);
byte[] plaintext = new byte[ciphertext.length - IV_SIZE];
aesCTR.processBytes(ciphertext, IV_SIZE, plaintext.length, plaintext, 0);
return new String(plaintext, "UTF-8");
}
}
Counter mode does not require padding and is fully online, so you only have to call processBytes
. For CBC mode you should look at PaddedBufferedBlockCipher
. Still you would have slightly a tiny amount of buffer handling during decryption: before decryption you don't know the amount of padding that is present.
You could remove the IV code and the UTF-8 character decoding + exception handling, but you would be insecure and possibly incompatible. This code prefixes the IV to the ciphertext.
回答2:
BouncyCastle implements the "normal Java API" so you can use Cipher.doFinal(String.getBytes())
you just have to specify the Provider "BC" when getting the Cipher: Cipher.getInstance("YourTransformation", "BC")
回答3:
Use Bouncy Castle's CipherOutputStream. It's the closest thing to the Java API.
static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
public static void main(String[] args) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("Threefish-1024", PROVIDER);
kg.init(1024);
KeyParameter key = new KeyParameter(kg.generateKey().getEncoded());
byte[] tweak = new byte[16];
TweakableBlockCipherParameters params = new TweakableBlockCipherParameters(key, tweak);
byte[] plaintext = "Hi! I'm cat!".getBytes();
byte[] ciphertext = encrypt(params, plaintext);
System.out.println(new String(decrypt(params, ciphertext)));
// prints "Hi! I'm cat!"
}
static byte[] encrypt(TweakableBlockCipherParameters params, byte[] plaintext) throws Exception {
return encryptOrDecrypt(true, params, plaintext);
}
static byte[] decrypt(TweakableBlockCipherParameters params, byte[] ciphertext) throws Exception {
return encryptOrDecrypt(false, params, ciphertext);
}
static byte[] encryptOrDecrypt(boolean encrypt, TweakableBlockCipherParameters params, byte[] bytes) throws Exception {
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(
new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024)), new PKCS7Padding());
cipher.init(encrypt, params);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CipherOutputStream cos = new CipherOutputStream(baos, cipher);
cos.write(bytes);
// calling CipherOutputStream.close is mandatory
// it acts like Cipher.doFinal
cos.close();
return baos.toByteArray();
}
Link to my answer to a similar and related question.
来源:https://stackoverflow.com/questions/30511903/what-is-the-simplest-way-to-encrypt-decrypt-a-byte-array-using-bouncycastles-b