为了账号安全,请及时绑定邮箱和手机立即绑定

使用 AES 的角度加密与旧的 Java 代码有不同的结果

使用 AES 的角度加密与旧的 Java 代码有不同的结果

白板的微信 2023-03-17 16:19:19
所以通常我使用一个 java 文件用 AES 将字符串加密和解密为十六进制,然后我的角度应用程序想要使用 api,使用它的结果。这是我的旧 Java 代码package decryptoor;import java.io.UnsupportedEncodingException;import java.security.MessageDigest;import java.util.Formatter;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;public class CryptoAndroidKoplak {    private static final String TEXT_ENCODING = "UTF-8";    private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";    private static final String ENCRYPTION_ALGORITM = "AES";    private static final String TAG = "Crypto";    private Cipher cipher;    private IvParameterSpec initialVector;//    private static void DEBUG(String msg){//        if(IDefines.DEBUG_LOG_TRACE){//            Log.i(TAG, msg);//        }//    }    public CryptoAndroidKoplak() {        try {            cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);            initialVector = new IvParameterSpec(new byte[16]);        } catch (Exception e) {            System.out.println(e.toString());        }    }    public String encryptString(String plainText, String key) throws Exception {        return toHexString(encrypt(plainText, key)).toUpperCase();    }    public byte[] encrypt(String plainText, String key) throws Exception {        byte[] byteKey = getKeyBytes(key);        byte[] plainData = plainText.getBytes(TEXT_ENCODING);        SecretKeySpec keySpec = new SecretKeySpec(byteKey, ENCRYPTION_ALGORITM);        cipher.init(Cipher.ENCRYPT_MODE, keySpec, initialVector);        return cipher.doFinal(plainData);    }    public String decryptString(String encryptedText, String key) throws Exception {        return new String(decrypt(encryptedText, key));    }我已经尝试了三个代码,第一个不返回十六进制,所以我尝试了另外两种方法,但它没有显示与旧 Java 代码相同的加密字符串,所以我无法使用 api。知道为什么会这样吗?如果您有更好的方法来使用在角度和 Java 中都更简单的密钥进行加密和解密,那将非常有帮助。非常感谢
查看完整描述

1 回答

?
MMMHUHU

TA贡献1834条经验 获得超8个赞

在放弃如何使它与我的旧 Java 代码相同之后,最后我尝试制作一个新的 hehe...

所以在我阅读 这个答案之后,我明白了 CryptoJS(我在角度中使用的库)实现了与 OpenSSL 相同的密钥派生功能。所以我选择像这样使用基本的 CryptoJS 函数来加密我的字符串

var text = "The quick brown fox jumps over the lazy dog. 👻 👻";

var secret = "René Über";

var encrypted = CryptoJS.AES.encrypt(text, secret);

encrypted = encrypted.toString();

console.log("Cipher text: " + encrypted);

在那之后,我需要做的是制作新的 java 文件来加密和解密 aes OpenSsl,我在这个答案中得到了我需要的东西。我使用罗伯特回答,因为接受的答案并没有真正给我我需要的东西。

但是就像第一个答案提到的那样,要以这种方式加密和解密,我们必须安装 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy。否则,密钥大小为 256 的 AES 将无法工作并抛出异常:(您不需要具有最新 Java 版本的 JCE)

所以我添加了一些功能来强制使用密钥大小为 256 的 AES,而无需在此处安装 JCE 。注意使用这个,实际上不推荐,请阅读 ericson answer 中的评论

然后这是我像 OpenSsl 一样加密和解密的最终代码

package decryptoor;


import groovy.transform.CompileStatic;

import java.io.UnsupportedEncodingException;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.net.URLEncoder;

import javax.crypto.Cipher;

import javax.crypto.spec.IvParameterSpec;

import javax.crypto.spec.SecretKeySpec;

import java.security.MessageDigest;

import java.security.SecureRandom;

import static java.nio.charset.StandardCharsets.*;

import java.security.InvalidAlgorithmParameterException;

import java.security.InvalidKeyException;

import java.security.NoSuchAlgorithmException;

import java.security.Permission;

import java.security.PermissionCollection;

import java.util.Arrays;

import java.util.Base64;

import java.util.Map;

import javax.crypto.BadPaddingException;

import javax.crypto.IllegalBlockSizeException;

import javax.crypto.NoSuchPaddingException;


/**

* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a shared key (aka password) with symetric ciphers.

*/

@CompileStatic

class OpenSslAes {


/** OpenSSL's magic initial bytes. */

private static final String SALTED_STR = "Salted__";

private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);



static String encryptAndURLEncode(String password, String clearText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException, UnsupportedEncodingException {


    String encrypted = encrypt(password, clearText);

    return URLEncoder.encode(encrypted, UTF_8.name() );

}


/**

 *

 * @param password  The password / key to encrypt with.

 * @param data      The data to encrypt

 * @return  A base64 encoded string containing the encrypted data.

 */

static String encrypt(String password, String clearText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException {

    removeCryptographyRestrictions();

    final byte[] pass = password.getBytes(US_ASCII);

    final byte[] salt = (new SecureRandom()).generateSeed(8);

    final byte[] inBytes = clearText.getBytes(UTF_8);


    final byte[] passAndSalt = array_concat(pass, salt);

    byte[] hash = new byte[0];

    byte[] keyAndIv = new byte[0];

    for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {

        final byte[] hashData = array_concat(hash, passAndSalt);

        final MessageDigest md = MessageDigest.getInstance("MD5");

        hash = md.digest(hashData);

        keyAndIv = array_concat(keyAndIv, hash);

    }


    final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);

    final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);

    final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");


    final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

    byte[] data = cipher.doFinal(inBytes);

    data =  array_concat(array_concat(SALTED_MAGIC, salt), data);

    return Base64.getEncoder().encodeToString( data );

}


/**

 * @see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption  for what looks like a useful answer.  The not-yet-commons-ssl also has an implementation

 * @param password

 * @param source The encrypted data

 * @return

 */

static String decrypt(String password, String source) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {

    removeCryptographyRestrictions();

    final byte[] pass = password.getBytes(US_ASCII);


    final byte[] inBytes = Base64.getDecoder().decode(source);


    final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);

    if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {

        throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");

    }


    final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);


    final byte[] passAndSalt = array_concat(pass, salt);


    byte[] hash = new byte[0];

    byte[] keyAndIv = new byte[0];

    for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {

        final byte[] hashData = array_concat(hash, passAndSalt);

        final MessageDigest md = MessageDigest.getInstance("MD5");

        hash = md.digest(hashData);

        keyAndIv = array_concat(keyAndIv, hash);

    }


    final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);

    final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");


    final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);


    final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));

    final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);

    return new String(clear, UTF_8);

}



private static byte[] array_concat(final byte[] a, final byte[] b) {

    final byte[] c = new byte[a.length + b.length];

    System.arraycopy(a, 0, c, 0, a.length);

    System.arraycopy(b, 0, c, a.length, b.length);

    return c;

}


private static void removeCryptographyRestrictions() {

    if (!isRestrictedCryptography()) {

        return;

    }

    try {

        /*

         * Do the following, but with reflection to bypass access checks:

         * 

         * JceSecurity.isRestricted = false; JceSecurity.defaultPolicy.perms.clear();

         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);

         */

        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");

        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");

        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");


        Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");

        isRestrictedField.setAccessible(true);

        setFinalStatic(isRestrictedField, true);

        isRestrictedField.set(null, false);


        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");

        defaultPolicyField.setAccessible(true);

        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);


        final Field perms = cryptoPermissions.getDeclaredField("perms");

        perms.setAccessible(true);

        ((Map<?, ?>) perms.get(defaultPolicy)).clear();


        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");

        instance.setAccessible(true);

        defaultPolicy.add((Permission) instance.get(null));

    }

    catch (final Exception e) {

        e.printStackTrace();

    }

}


static void setFinalStatic(Field field, Object newValue) throws Exception {

      field.setAccessible(true);


      Field modifiersField = Field.class.getDeclaredField("modifiers");

      modifiersField.setAccessible(true);

      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);


      field.set(null, newValue);

   }


private static boolean isRestrictedCryptography() {

    // This simply matches the Oracle JRE, but not OpenJDK.

    return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));

}

}


查看完整回答
反对 回复 2023-03-17
  • 1 回答
  • 0 关注
  • 128 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信