Angular AES 加密与旧 Java 代码的结果不同
Angular Encryption with AES has different result with old Java Code
所以我通常使用一个 java 文件来使用 AES 将字符串加密和解密为十六进制,
然后我的 angular 应用程序想要使用 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;
}
}
这是我在 angular
中的代码
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;
}
}
我已经尝试了三个代码,第一个不是 return 十六进制,所以我尝试了另外两种方法,但它没有显示与旧 java 代码相同的加密字符串,所以我不能消耗 api.
知道为什么会这样吗?
如果您有更好的方法来使用 angular 和 java 中更简单的密钥进行加密和解密,那将非常有帮助。
非常感谢
在放弃如何使它与我的旧 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 文件来加密和解密 aes OpenSsl,我得到了我需要的 。我使用 robert answer,因为接受的答案并不能真正满足我的需求。
但是就像第一个答案提到的那样,要以这种方式加密和解密,我们必须安装 Java 加密扩展 (JCE) 无限强度管辖策略。否则,密钥大小为 256 的 AES 将无法工作并抛出异常:(您不需要 up-to-date java 版本的 JCE)
所以我添加了一些功能来强制使用密钥大小为 256 的 AES 而无需安装 JCE here。注意使用这个,实际上不推荐,请阅读 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 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"));
}
}
所以我通常使用一个 java 文件来使用 AES 将字符串加密和解密为十六进制, 然后我的 angular 应用程序想要使用 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;
}
}
这是我在 angular
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;
}
}
我已经尝试了三个代码,第一个不是 return 十六进制,所以我尝试了另外两种方法,但它没有显示与旧 java 代码相同的加密字符串,所以我不能消耗 api.
知道为什么会这样吗? 如果您有更好的方法来使用 angular 和 java 中更简单的密钥进行加密和解密,那将非常有帮助。
非常感谢
在放弃如何使它与我的旧 java 代码相同之后,最后我尝试制作一个新的呵呵...
所以在我阅读
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 加密扩展 (JCE) 无限强度管辖策略。否则,密钥大小为 256 的 AES 将无法工作并抛出异常:(您不需要 up-to-date java 版本的 JCE)
所以我添加了一些功能来强制使用密钥大小为 256 的 AES 而无需安装 JCE here。注意使用这个,实际上不推荐,请阅读 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 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"));
}
}