Android中的加密相当于php的MCRYPT_RIJNDAEL_256

时间:2013-08-07 10:53:35

标签: php android encryption

我使用下面的PHP代码进行加密:


$enc_request = base64_encode(
    mcrypt_encrypt(MCRYPT_RIJNDAEL_256, 
                 $this->_app_key, 
                 json_encode($request_params), 
                 MCRYPT_MODE_ECB)
);

现在尝试加密in android并获取不同的加密字符串。下面是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中获得相同的正确加密字符串。

1 个答案:

答案 0 :(得分:4)

我使用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希望你总是需要填充,而PHP则不然。所以Bouncy增加1..n个字节,而PHP增加0 ..(n-1)个字节,其中n是块大小(Rijndael-256/256为32个字节)。所以你可能不得不自己填充/取消填充;一定要测试边缘情况!