【问题标题】:Plain Text Encrypt from Dart code but Decrypt from Java code纯文本从 Dart 代码加密,但从 Java 代码解密
【发布时间】:2022-10-12 21:48:51
【问题描述】:

当我使用 dart 加密纯文本时,加密的文本是从 java 代码解密然后得到这个错误:

javax.crypto.BadPaddingException: pad block corrupted
  at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
  at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
  at javax.crypto.Cipher.doFinal(Cipher.java:2168)
  at AesUtil.doFinal(AesUtil.java:75)
  at AesUtil.decrypt(AesUtil.java:60)
  at Main.main(Main.java:18)

相同的 Iv、Salt 和 Passphase 值使用 java 端生成密钥,但密钥生成不同,而且密码测试生成不同,但我使用相同的方法生成密钥。我不知道我错过了什么 在飞镖代码方面 请帮我。

      dependencies:
          encrypt: ^5.0.1
          hex: ^0.2.0
          password_hash_plus: ^4.0.0```

飞镖代码是:

      var random = Random.secure();
      var values = List<int>.generate(16, (i) => random.nextInt(255));

      // final salt = aes.IV.fromSecureRandom(16);
      final salt = hex.encode(values);
      final generator = PBKDF2(hashAlgorithm: sha1);
      final key = aes.Key.fromBase64(generator.generateBase64Key("1234567891234567", salt, 1000, 16));

     
      final iv = aes.IV.fromSecureRandom(16);
    
      final encrypter =
          aes.Encrypter(aes.AES(key, mode: aes.AESMode.cbc, padding: 'PKCS7'));
      final encrypted = encrypter.encrypt(st.password!, iv: iv);

      var str = '${iv.base16}::${salt}::${encrypted.base64}';
      var bytes = utf8.encode(str);
      var base64Str = base64.encode(bytes);


      //final decrypt = encrypter.decrypt64("/vvAYMc3rgCvPvuSVU/qQw==", iv: iv);

      print(
          '------------------------------,\n encrypt ${(encrypted.base64)}-----------'
           //'--\ndecrypted ${decrypt}-----------base64--------$base64Str-----'
          '\nkey = ${key.base64} array--\niv = ${iv.base16}--salt= {${salt}');

Java代码是:

class Main {
    public static void main(String[] args) {
        AesUtil aesUtil = new AesUtil();
        String encrypt = aesUtil.encrypt("b9266c74df614967d9acaa2878bff87c", "6ab7c799d6411f9d0c8e048ad526eeee", "1234567891234567", "Jitu@123456");
        String a = aesUtil.decrypt("01e6a073a4255c92e704bd94d76d75c5", "98a21e07ed34afc523c5f5938c9202db", "1234567891234567", "MumTfpnzZh9bk94yiTuA+g==");

        System.out.println("encrypt = " + encrypt + " \ndecrpty valaue----" + a);
    }
}

java的加密代码:

// Online Java Compiler
// Use this editor to write, compile and run your Java code online

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;

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

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Component;

public class AesUtil {
    private final int keySize;
    private final int iterationCount;
    private final Cipher cipher;

    public AesUtil() {
        this.keySize = 128;
        this.iterationCount = 1000;
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            e.printStackTrace();
            throw fail(e);
        } catch (NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
    }

    public String encrypt(String salt, String iv, String passphrase, String plaintext) {
        try {
            SecretKey key = generateKey(salt, passphrase);
            System.out.println("encryption key-------= " + base64(key.getEncoded()));
            byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("ISO-8859-1"));
            return base64(encrypted);
        } catch (UnsupportedEncodingException e) {
            throw fail(e);
        }
    }

    public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
        try {
            SecretKey key = generateKey(salt, passphrase);
            System.out.println("decrypt key-------= " + base64(key.getEncoded()));
            byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
            return new String(decrypted, "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
        try {
            IvParameterSpec IivParameterSpec = new IvParameterSpec(hex(iv));
            System.out.println("----iv--= " + hex(IivParameterSpec.getIV()));
            cipher.init(encryptMode, key, IivParameterSpec);
            return cipher.doFinal(bytes);
        } catch (InvalidKeyException
                 | InvalidAlgorithmParameterException
                 | IllegalBlockSizeException
                 | BadPaddingException e) {
            e.printStackTrace();
            return null;
        }
    }

    private SecretKey generateKey(String salt, String passphrase) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            byte[] s = hex(salt);
            System.out.println("salt-= " + hex(s));
            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), s, iterationCount, keySize);

            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
            return key;
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            return null;
        }
    }

    public static String random(int length) {
        byte[] salt = new byte[length];
        new SecureRandom().nextBytes(salt);
        return hex(salt);
    }

    public static String base64(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    public static byte[] base64(String str) {
        return Base64.decodeBase64(str);
    }

    public static String hex(byte[] bytes) {
        return Hex.encodeHexString(bytes);
    }

    public static byte[] hex(String str) {
        try {
            return Hex.decodeHex(str.toCharArray());
        } catch (DecoderException e) {
            throw new IllegalStateException(e);
        }
    }

    private IllegalStateException fail(Exception e) {
        return null;
    }

    public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {

        int digestLength = md.getDigestLength();
        int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
        byte[] generatedData = new byte[requiredLength];
        int generatedLength = 0;

        try {
            md.reset();

            // Repeat process until sufficient data has been generated
            while (generatedLength < keyLength + ivLength) {

                // Digest data (last digest if available, password data, salt if available)
                if (generatedLength > 0)
                    md.update(generatedData, generatedLength - digestLength, digestLength);
                md.update(password);
                if (salt != null)
                    md.update(salt, 0, 8);
                md.digest(generatedData, generatedLength, digestLength);

                // additional rounds
                for (int i = 1; i < iterations; i++) {
                    md.update(generatedData, generatedLength, digestLength);
                    md.digest(generatedData, generatedLength, digestLength);
                }

                generatedLength += digestLength;
            }

            // Copy key and IV into separate byte arrays
            byte[][] result = new byte[2][];
            result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
            if (ivLength > 0)
                result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);

            return result;

        } catch (DigestException e) {
            throw new RuntimeException(e);

        } finally {
            // Clean out temporary data
            Arrays.fill(generatedData, (byte) 0);
        }
    }
}

谢谢

【问题讨论】:

    标签: java flutter dart encryption


    【解决方案1】:

    问题是由于盐的编码不正确:

    • 在 Java 代码中,十六进制编码的盐在 generateKey() 中进行十六进制解码,生成的字节序列 (byte[]) 用于密钥派生。
    • 在 Dart 代码中,十六进制编码的盐被传递给 generateBase64Key()内部UTF-8 编码,s。 here。生成的字节序列 (List&lt;int&gt;) 用于密钥派生。

    因此两种代码都使用不同的字节序列进行密钥推导,产生不同的密钥,从而导致解密失败。

    由于 salt 可以是任意字节序列,但(通常)不能通过 UTF-8 编码从字符串生成任何字节序列,因此在password_hash_plusDart 库不合适。
    因此,您必须选择另一个 PBKDF2 实现,其中盐作为字节序列传递,就像在 Java 端一样,例如 PointyCastle 的 PBKDF2 实现:

    import 'package:pointycastle/export.dart';
    import 'dart:typed_data';
    ...
    final key = aes.Key(deriveKey("1234567891234567", Uint8List.fromList(values))); // Raw salt for key derivation!
    final salt = hex.encode(values);                                                // Hex encoded salt for output!
    ...
    Uint8List deriveKey(String passphrase, Uint8List salt){
        Uint8List passphraseBytes = Uint8List.fromList(utf8.encode(passphrase));
        KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64));    // 64 byte block size
        Pbkdf2Parameters params = Pbkdf2Parameters(salt, 1000, 16);             // 16 byte key size
        derivator.init(params);
        return derivator.process(passphraseBytes);
    }
    ...
    

    Dart 代码返回 salt 和 IV 十六进制编码以及密文 Base64 编码。如果将此数据与这些编码一起传递给 Java 代码的 decrypt() 方法,则解密成功。

    请注意,对于 PBKDF2,1000 的迭代计数通常太小。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-25
      • 2010-12-13
      • 2023-04-05
      • 2021-08-06
      • 2023-02-07
      • 1970-01-01
      • 2015-12-18
      相关资源
      最近更新 更多