我正在使用下面的 php 代码进行加密:
$enc_request = base64_encode(
mcrypt_encrypt(MCRYPT_RIJNDAEL_256,
$this->_app_key,
json_encode($request_params),
MCRYPT_MODE_ECB)
);
现在尝试在 android 中加密并获取不同的加密字符串。下面是安卓代码:
public void enc(){
byte[] rawKey = getRawKey("my_key".getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal("my_message".getBytes());
String result=Base64.encodeToString(encrypted, Base64.DEFAULT);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(256, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
谁能帮帮我,我哪里错了?并在 android 中也获得相同正确的加密字符串。
最佳答案
我使用 Bouncy CaSTLe 在 Java 中创建了一个 main
方法来展示您的代码示例中使用的 mcrypt_encrypt()
的内部工作原理。
这主要是为了让其他开发者知道 PHP 的 mcrypt_encrypt()
是一个非常危险的方法。它不会失败太多,但那是因为它宁愿在很久以前就应该停止的地方继续。例如,它从键中添加或删除值。它会在执行此操作时发出警告,但不会直接显示在代码中。
public static void main(String[] args) throws DataLengthException, IllegalStateException, InvalidCipherTextException {
// just some constants
boolean ENCRYPT = true;
boolean DECRYPT = false;
// the key is either in binary in PHP or a string (dynamic isn't it?), lets assume ASCII
byte[] givenKey = args[0].getBytes(Charset.forName("ASCII"));
// determine the key size dynamically, somebody thought this was a good idea...
// NOTE: PHP will emit a warning if the key size is larger, but will simply use the
// largest key size otherwise
final int keysize;
if (givenKey.length <= 128 / Byte.SIZE) {
keysize = 128;
} else if (givenKey.length <= 192 / Byte.SIZE) {
keysize = 192;
} else {
keysize = 256;
}
// create a 256 bit key by adding zero bytes to the decoded key
byte[] keyData = new byte[keysize / Byte.SIZE];
System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
KeyParameter key = new KeyParameter(keyData);
// create a Rijndael cipher with 256 bit block size, this is not AES
BlockCipher rijndael = new RijndaelEngine(256);
// use a padding method that only works on data that cannot end with zero valued bytes
ZeroBytePadding c = new ZeroBytePadding();
// use ECB mode encryption, which should never be used
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
// initialize the cipher using the key (no need for an IV, this is ECB)
pbbc.init(ENCRYPT, key);
// create a plain text byte array
byte[] plaintext = args[1].getBytes(Charset.forName("UTF8"));
// create a buffer for the ciphertext
byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
int offset = 0;
offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
offset += pbbc.doFinal(ciphertext, offset);
// show the ciphertext
System.out.println(new String(Hex.encode(ciphertext), Charset.forName("ASCII")));
// reverse the encryption
pbbc.init(DECRYPT, key);
byte[] decrypted = new byte[pbbc.getOutputSize(ciphertext.length)];
offset = 0;
offset += pbbc.processBytes(ciphertext, 0, ciphertext.length, decrypted, offset);
offset += pbbc.doFinal(decrypted, offset);
// this will probably print out correctly, but it isn't actually correct
System.out.println(new String(decrypted, Charset.forName("UTF8")));
// check out the zero's at the end
System.out.println(new String(Hex.encode(decrypted), Charset.forName("UTF8")));
// so lets make it a bit shorter... the PHP way
// note that in PHP, the string may *not* contain a null terminator
// add it yourself before printing the string
System.out.println(new String(decrypted, Charset.forName("UTF8")).replaceAll("\\x00+$", ""));
}
警告:以上代码包含ZeroBytePadding
。后来我发现,Bouncy CaSTLe 和 PHP 在这方面是有区别的:Bouncy CaSTLe 期望你总是需要 pad,而 PHP 则不需要。所以 Bouncy 添加 1..n 字节,而 PHP 添加 0..(n-1) 字节,其中 n 是 block 大小(Rijndael-256/256 为 32 字节)。因此,您可能必须自己进行填充/取消填充;一定要测试边缘情况!
关于php - Android中的加密相当于php的MCRYPT_RIJNDAEL_256,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18101545/