华硕zenfone5 t00j AES 256解密问题
ASUS zenfone5 t00j AES 256 decryption issue
我正在开发移动应用程序和客户端,我们在服务器端使用 JavaScript (kony) java。这适用于除英特尔芯片组设备(华硕 Zenfone)之外的所有其他设备。 PFB 加密的JS代码
function encryptDataModeCBC()
{
var encData = "Test";
try
{
var encText = CryptoJS.AES.encrypt(encData, "3f4c57006f7d2d9528de3c46b626df06cdc405cb0243b10ca7612d967c688744", {
iv: "31fd1ae51454cd55db81f1fa60a343ed",
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString(CryptoJS.enc.Base64);
alert ("encText => "+encText);
kony.print("$$$$ encText => "+encText);
}
catch (e)
{
alert(kony.i18n.getLocalizedString("technicalError"));
}
}
此处使用 sha256 和 sha512 哈希算法创建 IV 和密钥。
PFB 我们在服务器端用于解密加密字符串的代码片段
密钥生成代码
private SecretKeySpec getKey(String mode, String msgDigest, String encryptionKey, boolean is256) throws Exception {
byte[] key = encryptionKey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance(msgDigest); // This is SHA-256
key = sha.digest(key);
if (is256) { // This is true in our case.
key = Arrays.copyOf(key, 32);
this.logger.debug("Secret Key " + DigestUtils.sha256Hex(encryptionKey).substring(0, 32));
} else {
key = Arrays.copyOf(key, 16);
this.logger.debug("Secret Key " + DigestUtils.sha256Hex(encryptionKey).substring(0, 16));
}
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
String modeStr = mode.equals("ECB") ? "AES/ECB/PKCS5Padding" : "AES/CBC/PKCS5Padding";
cipher = Cipher.getInstance(modeStr);
return secretKeySpec;
}
服务器端IV生成
private IvParameterSpec getIV(String uid, String pin) throws Exception {
String ivValue = new StringBuilder(uid).reverse().toString() + new StringBuilder(pin).reverse();
byte[] key = ivValue.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-256");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
IvParameterSpec iv = new IvParameterSpec(key);
return iv;
}
正如我上面提到的,这在英特尔芯片组设备中失败了。这是我在解密字符串
时遇到的异常
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
当我尝试加密字符串 "Test" 时,我得到 "Tn2SzI8dmgCmEvQrzdqLxw==" 作为我在下面 java 代码中使用的加密字符串,并尝试解密我得到以下错误的地方
enc text => 7b9UNDI4IWNITNAQlYNP8w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.ust.Encryptor.decrypt(Encryptor.java:92)
at com.ust.Encryptor.main(Encryptor.java:113)
这是我用来解密
的JAVA代码
package com.ust;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
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;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
public class Encryptor {
private static final String AES_PASS = "0ca763dc6b05b5230e44beb6b90e346440204b6d334b09623eafd3fcfbad6a302faca28b0994872e3fd782e7353026684b7ac9385662144e0ed1e2a8e3e14fab79059929681e3794eb97271328ecccda6dbfb3a7991ea1324615cf5908fabdf6"; // Hashed into an AES key later
private SecretKeySpec keyObj;
private Cipher cipher;
private IvParameterSpec ivObj;
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public Encryptor() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException {
// A constant IV, since CBC requires an IV but we don't really need one
String ivValue = new StringBuilder("astring").reverse().toString() + new StringBuilder("0ca763dc6b05b5230e44beb6b90e346440204b6d334b09623eafd3fcfbad6a302faca28b0994872e3fd782e7353026684b7ac9385662144e0ed1e2a8e3e14fab").reverse();
System.out.println("ivValue => "+ivValue);
try {
byte[] ivkey = ivValue.getBytes("UTF-8");
MessageDigest shaIv = MessageDigest.getInstance("SHA-256");
ivkey = shaIv.digest(ivkey);
ivkey = Arrays.copyOf(ivkey, 16);
System.out.println("IV => "+bytesToHex(ivkey));
this.ivObj = new IvParameterSpec(ivkey);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Create an SHA-256 256-bit hash of the key
byte[] key = AES_PASS.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
key = sha.digest(key);
key = Arrays.copyOf(key, 32); // Use only first 256 bit
System.out.println("SEC KEY => "+bytesToHex(key));
this.keyObj = new SecretKeySpec(key, "AES");
// Create a Cipher by specifying the following parameters
// a. Algorithm name - here it is AES
// b. Mode - here it is CBC mode
// c. Padding - e.g. PKCS7 or PKCS5
this.cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
}
public String encrypt(String strDataToEncrypt) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
String strCipherText = new String();
this.cipher.init(Cipher.ENCRYPT_MODE, this.keyObj, this.ivObj);
// Encrypt the Data
// a. Declare / Initialize the Data. Here the data is of type String
// b. Convert the Input Text to Bytes
// c. Encrypt the bytes using doFinal method
byte[] byteDataToEncrypt = strDataToEncrypt.getBytes();
byte[] byteCipherText = this.cipher.doFinal(byteDataToEncrypt);
// b64 is done differently on Android
strCipherText = Base64.encodeBase64String(byteCipherText);
return strCipherText;
}
public String decrypt(String strCipherText) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
String strDecryptedText = new String();
// Initialize the Cipher for Encryption
this.cipher.init(Cipher.DECRYPT_MODE, this.keyObj, this.ivObj);
// Decode the Base64 text
byte[] cipherBytes = Base64.decodeBase64(strCipherText);
// Decrypt the Data
// a. Initialize a new instance of Cipher for Decryption (normally don't reuse the same object)
// Be sure to obtain the same IV bytes for CBC mode.
// b. Decrypt the cipher bytes using doFinal method
byte[] byteDecryptedText = this.cipher.doFinal(cipherBytes);
strDecryptedText = new String(byteDecryptedText);
return strDecryptedText;
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static void main (String args[]) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException{
Encryptor aesCipher = new Encryptor();
try {
String encText = aesCipher.encrypt("Test");
System.out.println("enc text => "+encText);
String plaintext = aesCipher.decrypt("Tn2SzI8dmgCmEvQrzdqLxw==");//("eat6f1uCCXVqJgTNUA8BCqXSA4kG4GhKajXdkyV0TewK+jgDkbQ/lPVaevv4rW3XdSmtVyOKLVJjPw9Akeblrh+ejIv9u48n7PkRKniwfxq/URuPU7lhS/sO5JMiJ7+ufgKFvJapxhSfftCtigtDc8F6Y2lJIPEUeQeQKOVc1noeLqPFggz55hWjWvDtpYh/sG76MwLlWDM7cj+uu6ru3ImmDA7qoM4tJOWBBkfng8u20R1ZcF3gM45TgDLUdL912AE1WO+grGBGjqzTXlK2/jgu3OOsLVI0jndB49K5q3/oKJc7JEoIZb0eZJcuZ80A");
System.out.println("plain text => "+plaintext);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
CryptoJS 假设
- 作为字符串传递的密钥实际上是一个密码,它将与随机生成的盐一起再次散列,或者如果它是 WordArray 和
,它将按原样使用密钥
- IV 应该是 WordArray
WordArray 是 CryptoJS 的内部二进制数据表示。
代码应该是:
try {
var key = CryptoJS.enc.Hex.parse("3f4c57006f7d2d9528de3c46b626df06cdc405cb0243b10ca7612d967c688744");
var iv = CryptoJS.enc.Hex.parse("31fd1ae51454cd55db81f1fa60a343ed44");
var encText = CryptoJS.AES.encrypt(encData, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString(CryptoJS.enc.Base64);
alert ("encText => "+encText);
kony.print("$$$$ encText => "+encText);
}
catch (e)
{
alert(kony.i18n.getLocalizedString("technicalError"));
}
需要思考的事情:
如果您将对称密钥从服务器发送到客户端,那么可能正在监听的任何人都将获得密钥并可以解密您发送的密文。此解决方案不提供安全性,而是提供混淆。您应该使用 TLS,这将使连接真正安全。
IV 必须是不可预测的(读作:随机)。不要使用静态 IV,因为这会使密码具有确定性,因此在语义上不安全。观察密文的攻击者可以确定之前发送相同消息前缀的时间。 IV 不是秘密的,因此您可以将它与密文一起发送。通常,它只是简单地添加到密文中并在解密之前被切掉。
最好对您的密文进行身份验证,以便像 padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC 方案一样进行攻击。
我正在开发移动应用程序和客户端,我们在服务器端使用 JavaScript (kony) java。这适用于除英特尔芯片组设备(华硕 Zenfone)之外的所有其他设备。 PFB 加密的JS代码
function encryptDataModeCBC()
{
var encData = "Test";
try
{
var encText = CryptoJS.AES.encrypt(encData, "3f4c57006f7d2d9528de3c46b626df06cdc405cb0243b10ca7612d967c688744", {
iv: "31fd1ae51454cd55db81f1fa60a343ed",
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString(CryptoJS.enc.Base64);
alert ("encText => "+encText);
kony.print("$$$$ encText => "+encText);
}
catch (e)
{
alert(kony.i18n.getLocalizedString("technicalError"));
}
}
此处使用 sha256 和 sha512 哈希算法创建 IV 和密钥。
PFB 我们在服务器端用于解密加密字符串的代码片段
密钥生成代码
private SecretKeySpec getKey(String mode, String msgDigest, String encryptionKey, boolean is256) throws Exception {
byte[] key = encryptionKey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance(msgDigest); // This is SHA-256
key = sha.digest(key);
if (is256) { // This is true in our case.
key = Arrays.copyOf(key, 32);
this.logger.debug("Secret Key " + DigestUtils.sha256Hex(encryptionKey).substring(0, 32));
} else {
key = Arrays.copyOf(key, 16);
this.logger.debug("Secret Key " + DigestUtils.sha256Hex(encryptionKey).substring(0, 16));
}
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
String modeStr = mode.equals("ECB") ? "AES/ECB/PKCS5Padding" : "AES/CBC/PKCS5Padding";
cipher = Cipher.getInstance(modeStr);
return secretKeySpec;
}
服务器端IV生成
private IvParameterSpec getIV(String uid, String pin) throws Exception {
String ivValue = new StringBuilder(uid).reverse().toString() + new StringBuilder(pin).reverse();
byte[] key = ivValue.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-256");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
IvParameterSpec iv = new IvParameterSpec(key);
return iv;
}
正如我上面提到的,这在英特尔芯片组设备中失败了。这是我在解密字符串
时遇到的异常javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
当我尝试加密字符串 "Test" 时,我得到 "Tn2SzI8dmgCmEvQrzdqLxw==" 作为我在下面 java 代码中使用的加密字符串,并尝试解密我得到以下错误的地方
enc text => 7b9UNDI4IWNITNAQlYNP8w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.ust.Encryptor.decrypt(Encryptor.java:92)
at com.ust.Encryptor.main(Encryptor.java:113)
这是我用来解密
的JAVA代码package com.ust;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
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;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
public class Encryptor {
private static final String AES_PASS = "0ca763dc6b05b5230e44beb6b90e346440204b6d334b09623eafd3fcfbad6a302faca28b0994872e3fd782e7353026684b7ac9385662144e0ed1e2a8e3e14fab79059929681e3794eb97271328ecccda6dbfb3a7991ea1324615cf5908fabdf6"; // Hashed into an AES key later
private SecretKeySpec keyObj;
private Cipher cipher;
private IvParameterSpec ivObj;
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public Encryptor() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException {
// A constant IV, since CBC requires an IV but we don't really need one
String ivValue = new StringBuilder("astring").reverse().toString() + new StringBuilder("0ca763dc6b05b5230e44beb6b90e346440204b6d334b09623eafd3fcfbad6a302faca28b0994872e3fd782e7353026684b7ac9385662144e0ed1e2a8e3e14fab").reverse();
System.out.println("ivValue => "+ivValue);
try {
byte[] ivkey = ivValue.getBytes("UTF-8");
MessageDigest shaIv = MessageDigest.getInstance("SHA-256");
ivkey = shaIv.digest(ivkey);
ivkey = Arrays.copyOf(ivkey, 16);
System.out.println("IV => "+bytesToHex(ivkey));
this.ivObj = new IvParameterSpec(ivkey);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Create an SHA-256 256-bit hash of the key
byte[] key = AES_PASS.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
key = sha.digest(key);
key = Arrays.copyOf(key, 32); // Use only first 256 bit
System.out.println("SEC KEY => "+bytesToHex(key));
this.keyObj = new SecretKeySpec(key, "AES");
// Create a Cipher by specifying the following parameters
// a. Algorithm name - here it is AES
// b. Mode - here it is CBC mode
// c. Padding - e.g. PKCS7 or PKCS5
this.cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
}
public String encrypt(String strDataToEncrypt) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
String strCipherText = new String();
this.cipher.init(Cipher.ENCRYPT_MODE, this.keyObj, this.ivObj);
// Encrypt the Data
// a. Declare / Initialize the Data. Here the data is of type String
// b. Convert the Input Text to Bytes
// c. Encrypt the bytes using doFinal method
byte[] byteDataToEncrypt = strDataToEncrypt.getBytes();
byte[] byteCipherText = this.cipher.doFinal(byteDataToEncrypt);
// b64 is done differently on Android
strCipherText = Base64.encodeBase64String(byteCipherText);
return strCipherText;
}
public String decrypt(String strCipherText) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException {
String strDecryptedText = new String();
// Initialize the Cipher for Encryption
this.cipher.init(Cipher.DECRYPT_MODE, this.keyObj, this.ivObj);
// Decode the Base64 text
byte[] cipherBytes = Base64.decodeBase64(strCipherText);
// Decrypt the Data
// a. Initialize a new instance of Cipher for Decryption (normally don't reuse the same object)
// Be sure to obtain the same IV bytes for CBC mode.
// b. Decrypt the cipher bytes using doFinal method
byte[] byteDecryptedText = this.cipher.doFinal(cipherBytes);
strDecryptedText = new String(byteDecryptedText);
return strDecryptedText;
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static void main (String args[]) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException{
Encryptor aesCipher = new Encryptor();
try {
String encText = aesCipher.encrypt("Test");
System.out.println("enc text => "+encText);
String plaintext = aesCipher.decrypt("Tn2SzI8dmgCmEvQrzdqLxw==");//("eat6f1uCCXVqJgTNUA8BCqXSA4kG4GhKajXdkyV0TewK+jgDkbQ/lPVaevv4rW3XdSmtVyOKLVJjPw9Akeblrh+ejIv9u48n7PkRKniwfxq/URuPU7lhS/sO5JMiJ7+ufgKFvJapxhSfftCtigtDc8F6Y2lJIPEUeQeQKOVc1noeLqPFggz55hWjWvDtpYh/sG76MwLlWDM7cj+uu6ru3ImmDA7qoM4tJOWBBkfng8u20R1ZcF3gM45TgDLUdL912AE1WO+grGBGjqzTXlK2/jgu3OOsLVI0jndB49K5q3/oKJc7JEoIZb0eZJcuZ80A");
System.out.println("plain text => "+plaintext);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
CryptoJS 假设
- 作为字符串传递的密钥实际上是一个密码,它将与随机生成的盐一起再次散列,或者如果它是 WordArray 和 ,它将按原样使用密钥
- IV 应该是 WordArray
WordArray 是 CryptoJS 的内部二进制数据表示。
代码应该是:
try {
var key = CryptoJS.enc.Hex.parse("3f4c57006f7d2d9528de3c46b626df06cdc405cb0243b10ca7612d967c688744");
var iv = CryptoJS.enc.Hex.parse("31fd1ae51454cd55db81f1fa60a343ed44");
var encText = CryptoJS.AES.encrypt(encData, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString(CryptoJS.enc.Base64);
alert ("encText => "+encText);
kony.print("$$$$ encText => "+encText);
}
catch (e)
{
alert(kony.i18n.getLocalizedString("technicalError"));
}
需要思考的事情:
如果您将对称密钥从服务器发送到客户端,那么可能正在监听的任何人都将获得密钥并可以解密您发送的密文。此解决方案不提供安全性,而是提供混淆。您应该使用 TLS,这将使连接真正安全。
IV 必须是不可预测的(读作:随机)。不要使用静态 IV,因为这会使密码具有确定性,因此在语义上不安全。观察密文的攻击者可以确定之前发送相同消息前缀的时间。 IV 不是秘密的,因此您可以将它与密文一起发送。通常,它只是简单地添加到密文中并在解密之前被切掉。
最好对您的密文进行身份验证,以便像 padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC 方案一样进行攻击。