Angular Encryption с AES имеет другой результат со старым кодом Java - PullRequest
0 голосов
/ 11 июля 2019

Так что обычно я использую один файл 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));
    }

    public byte[] decrypt(String encryptedText, String key) throws Exception {
        byte[] byteKey = getKeyBytes(key);
        byte[] encryptData = hexToAscii(encryptedText);
        SecretKeySpec keySpec = new SecretKeySpec(byteKey, ENCRYPTION_ALGORITM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, initialVector);
        return cipher.doFinal(encryptData);
    }

    public static String toMD5(String text) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] data = text.getBytes(TEXT_ENCODING);
        return toHexString(md.digest(data));
    }

    public static String toSHA1(String text) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] data = text.getBytes(TEXT_ENCODING);
        return toHexString(md.digest(data));
    }

    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);

        Formatter formatter = new Formatter(sb);
        for (byte b : bytes) {
            formatter.format("%02x", b);
        }

        return sb.toString();
    }

    private static byte[] hexToAscii(String hexStr) {
        byte[] buff = new byte[hexStr.length() / 2];
        int offset = 0;
        for (int i = 0; i < hexStr.length(); i += 2) {
            String str = hexStr.substring(i, i + 2);
            buff[offset++] = (byte) Integer.parseInt(str, 16);
        }

        return buff;
    }

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

}



а это мой код в угловых

import { Injectable } from '@angular/core';
import * as CryptoJS from 'crypto-js';

@Injectable({
  providedIn: 'root'
})
export class Encryption {
  constructor() {}

  encryptAesToString(stringToEncrypt: string, key: string): string {
    // first way
    // let encrypted;
    // try {
    //   encrypted = CryptoJS.AES.encrypt(JSON.stringify(stringToEncrypt), key);
    // } catch (e) {
    //   console.log(e);
    // }
    // encrypted = CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
    // return encrypted;

    // second way
    // var b64 = CryptoJS.AES.encrypt(stringToEncrypt, key).toString();
    // var e64 = CryptoJS.enc.Base64.parse(b64);
    // var eHex = e64.toString(CryptoJS.enc.Hex);
    // return eHex;

    // third way
    const key2 = CryptoJS.enc.Utf8.parse(key);
    const iv = CryptoJS.enc.Utf8.parse(key);
    const encrypted = CryptoJS.AES.encrypt(stringToEncrypt, key2, {
      keySize: 16,
      iv: iv,
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7,
    });
    let eHex = CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
    return encrypted;
  }

  decryptAesformString(stringToDecrypt: string, key: string): string {
    let decrypted: string = '';
    try {
      const bytes = CryptoJS.AES.decrypt(stringToDecrypt, key);
      if (bytes.toString()) {
        decrypted = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
      }
    } catch (e) {
      console.log(e);
    }
    return decrypted;
  }
}

У меня есть три кода, первый не возвращает шестнадцатеричный код, поэтому я пробую еще 2 способа, но он не показывает ту же зашифрованную строку со старым кодом Java, поэтому я не могу использовать API.

есть идеи, почему это произошло? если у вас есть лучший способ шифрования и дешифрования с помощью ключа, более простого как в angular, так и в java, это действительно поможет.

большое спасибо

1 Ответ

0 голосов
/ 12 июля 2019

после отказа от того, как сделать то же самое с моим старым кодом Java, наконец, я пытаюсь создать новый хе-хе ...

так что после того, как я прочитал этот ответ , тогдаЯ понимаю, что CryptoJS (библиотека, которую я использую в angular) реализует ту же функцию вывода ключей, что и 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 для шифрования и дешифрования OpenSsl, и я получаю то, что мне нужно здесь, в этом ответе .Я использую ответ Роберта, потому что принятый ответ на самом деле не дает мне то, что мне нужно.

но, как и в первом упомянутом ответе, для шифрования и дешифрования таким образом, мы должны установить Политику юрисдикции неограниченной силы Java Cryptography Extension (JCE).В противном случае AES с размером ключа 256 не будет работать и выдает исключение: (вам не понадобится JCE с актуальной версией Java)

, поэтому я добавлю некоторые функции, чтобы принудительно использовать AES с ключомразмер 256 без установки JCE здесь .обратите внимание, чтобы использовать это, на самом деле не рекомендуется, пожалуйста, прочитайте комментарий в ответе Эриксона

тогда это мой последний код для шифрования и дешифрования, как 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"));
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...