【问题标题】:Rewrite PHP Rijndael algorithm to Java (Android)将 PHP Rijndael 算法重写为 Java (Android)
【发布时间】:2015-03-11 08:19:42
【问题描述】:

我需要在Java和php中编码一个字符串,结果必须相同。

给出以下条件:

  1. 算法:RIJNDAEL-128
  2. 密钥:5P443m2Q1R9A7f5r3e1z08642
  3. 模式:欧洲央行
  4. 初始化向量:N/A(因为我们使用 ECB,IV 被忽略)

String to encode: 201412181656005P443m2Q1R9A7f5r3e1z08642

PHP

 <?php
        class Cipher
        {
            private $securekey, $iv;

            function __construct($textkey)
            {
                $this->securekey = $textkey;
                $this->iv = mcrypt_create_iv(32);
            }

            function encryptR($input)
            {
                $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->securekey, $input, MCRYPT_MODE_ECB, $this->iv);
                return base64_encode($enc);
            }

            function decryptR($input)
            {
                return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->securekey, base64_decode($input), MCRYPT_MODE_ECB, $this->iv));
            }
        }

        $raw_text = '201412181656005P443m2Q1R9A7f5r3e1z08642';
        $secretKey = '5P443m2Q1R9A7f5r3e1z08642';

        $cipher = new Cipher($secretKey);
        $encrypted = $cipher->encryptR($raw_text);     
?>

输出:MbDHhIanWgySlMTOX+ItgVKudVLXbtj7ig2GMQacVM9JhyAPvVQxLJnHpEj/vhqW

JAVA

encrypted = encrypt("201412181656005P443m2Q1R9A7f5r3e1z08642","5P443m2Q1R9A7f5r3e1z08642");

public class Crypt {

    private final String characterEncoding = "UTF-8";
    private final String cipherTransformation = "AES/ECB/PKCS5Padding";
    private final String aesEncryptionAlgorithm = "AES";

    public  byte[] decrypt(byte[] cipherText, byte[] key) throws Exception
    {
        Cipher cipher = Cipher.getInstance(cipherTransformation);
        SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy);
        cipherText = cipher.doFinal(cipherText);
        return cipherText;
    }

    public byte[] encrypt(byte[] plainText, byte[] key) throws Exception
    {
        Cipher cipher = Cipher.getInstance(cipherTransformation);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        plainText = cipher.doFinal(plainText);
        return plainText;
    }

    private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
        byte[] keyBytes= new byte[16];
        byte[] parameterKeyBytes= key.getBytes(characterEncoding);
        System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
        return keyBytes;
    }

    @SuppressLint("NewApi")
    public String encrypt(String plainText, String key) throws Exception {
        byte[] plainTextbytes = plainText.getBytes(characterEncoding);
        byte[] keyBytes = getKeyBytes(key);
        // Log.i("iv", ""+keyBytesIV);
        return Base64.encodeToString(encrypt(plainTextbytes,keyBytes), Base64.DEFAULT);
    }

    @SuppressLint("NewApi")
    public String decrypt(String encryptedText, String key) throws Exception {
        byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT);
        byte[] keyBytes = getKeyBytes(key);

        return new String(decrypt(cipheredBytes, keyBytes), characterEncoding);
    }

}

输出:wd0FHYpLbgdpHhcSql7VVCiKWJWN5hvP0W9F4sgKWAWeDcSjvfKWTM5LHBCZJSRw

更新:

我将填充从 NoPadding 更改为 PKCS5Padding

这是正确的吗?我不确定,因为如果您查看 PHP 代码。没有指定任何填充(我自己基于语法的假设)。

Info on Mcrypt

其他见解:

阅读此document 关于填充(无填充)。一定与问题有关。

【问题讨论】:

  • 当我尝试运行您的代码时,出现以下异常:javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1016)
  • 平台之间的 AES/Rijndael 兼容性确实很困难。注意块的大小,你需要填充它并确保它被填充到最接近的可整除值 16。
  • 请注意,当 AES iv 大小为 16 字节时,您正在创建一个 32 字节的 iv。
  • 最好不要使用 mcrypt,它是废弃软件,多年未更新,不支持标准 PKCS#7 (née PKCS#5) 填充,仅甚至不能用于二进制数据的非标准空填充。 mcrypt 有许多出色的 bugs 可以追溯到 2003 年。请考虑使用 defuse,它正在维护并且是正确的。

标签: java php android aes rijndael


【解决方案1】:

看起来您的 PHP 版本使用 AES-128,根据定义,它使用 128 位(16 字节)密钥。但是看起来你传递了一个 25 字节的密钥 (5P443m2Q1R9A7f5r3e1z08642),我不确定 PHP 在发生这种情况时会做什么。

您的 Java 版本的 getKeyBytes() 方法仅返回所提供密钥的前 16 个字节,因此它仅使用它进行加密。

尝试将您的 PHP 版本中的密钥截断为 5P443m2Q1R9A7f5r,您会得到相同的结果。除了可能不同的末端部分。那时,问题将是填充。您可以在纯文本上应用 pkcs5_pad PHP 函数,使其与您的 Java 版本匹配。

说了这么多,如果这只是为了学习,没关系。否则,为了实际使用,您do not use ECB cipher mode 很重要。

【讨论】:

    【解决方案2】:

    我在getKeyBytes 方法中将byte[] keyBytes= new byte[16]; 更改为byte[] keyBytes= new byte[32]; 然后它工作正常。

    【讨论】:

      猜你喜欢
      • 2011-02-09
      • 2014-06-09
      • 2011-03-31
      • 2017-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-11
      相关资源
      最近更新 更多