繁体   English   中英

Java mcrypt_encrypt 的代码 PHP function 与 MCRYPT_RIJNDAEL_256 密码

[英]Java code for mcrypt_encrypt PHP function with MCRYPT_RIJNDAEL_256 cipher

我有一个用于加密字符串(令牌)的 PHP 代码。 我需要生成这个令牌并调用遗留后端 API。 后端 API 对此进行解密并对其进行验证,然后才允许访问 API。 我们正在 Java 中构建客户端应用程序。 所以这段代码需要在Java中实现。

$stringToEncode="****String which needs to be encrypted. This string length is multiple of 32********************";
$key="32ByteKey-asdcxzasdsadasdasdasda";
    echo base64_encode(
        mcrypt_encrypt(
                    MCRYPT_RIJNDAEL_256,
                    $key,
                    $stringToEncode,
                    MCRYPT_MODE_CBC,
                    $key
                )
            );

这里 IV 与 key 相同。 我尝试使用以下 Java 代码使用“org.bouncycastle.crypto”

private static void encrypt(String key, String data) throws InvalidCipherTextException {
            
        byte[] givenKey = key.getBytes(Charset.forName("ASCII"));        
        final int keysize = 256;
        byte[] keyData = new byte[keysize / Byte.SIZE];
        System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
        KeyParameter keyParameter = new KeyParameter(keyData);
        BlockCipher rijndael = new RijndaelEngine(256);
        ZeroBytePadding c = new ZeroBytePadding();
        PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
        CipherParameters ivAndKey = new ParametersWithIV(keyParameter, key.getBytes(Charset.forName("ASCII")));
        pbbc.init(true, ivAndKey);
        byte[] plaintext = data.getBytes(Charset.forName("UTF8"));
        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);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(ciphertext));
    }

但低于例外 -

Exception in thread "main" java.lang.IllegalArgumentException: invalid parameter passed to Rijndael init - org.bouncycastle.crypto.params.ParametersWithIV

我什至尝试使用“javax.crypto”,如下所示 -

import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class aes{
    
     public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
            String key = "32ByteKey-asdcxzasdsadasdasdasda";
            String data = "****String which needs to be encrypted. This string length is multiple of 32********************";
            encrypt(key, data);
        }
    
    public static void encrypt(String key1, String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
        
        byte[] key = key1.getBytes(Charset.forName("ASCII"));
        byte[] decrypted = data.getBytes(Charset.forName("UTF8"));      
        
        IvParameterSpec ivSpec = new IvParameterSpec(key);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
        byte[] encrypted = Base64.getEncoder().encodeToString(cipher.doFinal(decrypted)).getBytes();

        System.out.println(encrypted);
    }   
}

但得到以下例外 -

Exception in thread "main" java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long

有人可以建议我如何实现这个吗?

PHP 代码使用块大小为 256 位的 Rijndael。 SunJCE Provider 不支持,只有 AES(它是 Rindael 的一个子集,块大小为 128 位)。 由于这个原因,第二个 Java 代码片段也不起作用。 因此,必须应用像 BouncyCastle (BC) 这样的第三方提供商。

Java/BC 代码中有两个错误:

  • 与 PHP 代码相反,没有应用 CBC。 这必须使用CBCBlockCipher进行更改。
  • BC 的零填充变体与 mcrypt 变体不同。 BC 变体总是填充,即即使明文的长度已经对应于块大小的 integer 倍数(此处为 32 字节),mcrypt 变体不会(即后者仅在明文长度不填充时才填充)对应于块大小的 integer 倍数)。
    由于根据描述,明文已经对应于块大小的 integer 倍数,因此 mcrypt 变体不会填充。 在 Java / BC 代码中,如果根本不使用填充,则可以实现这一点。
    但要小心:如果明文的长度不对应于块大小的倍数 integer,当然必须使用填充(关于与 PHP 代码中使用的零填充变体的兼容性)。

这两个问题都可以通过更换线路来解决

ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);

经过

BufferedBlockCipher pbbc = new BufferedBlockCipher(new CBCBlockCipher(rijndael));

那么这两个代码产生的密文是相同的。

一些附加说明:

  • 将密钥用作 IV 通常没有用处。 由于出于安全原因,IV 只能用于给定密钥一次,因此此处必须为每次加密使用新密钥。 另请参见此处
    通常在实践中,每次加密都会生成一个新的随机 IV。
  • 通常使用标准更安全,即 AES 而不是块大小为 256 位的 Rijndael,这不是标准。
  • 零填充是不可靠的(此外,正如您所经历的那样,有几种变体可能会导致问题)。 更可靠的是 PKCS7 填充。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM