我在移动应用程序和客户端工作,我们在服务器端使用JavaScript(Kony)。除了英特尔芯片组设备(华硕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"));
}
}
在这里创建IV&使用SHA256&SHA512哈希算法。
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*..)
当我尝试对字符串"测试"进行加密时,我将获得" 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,则将使用键AS,并且
- 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不是秘密,因此您可以将其与密文一起发送。通常,它只是将其简单地添加到密文中,并在解密前切割。
最好对您的密文进行身份验证,以便无法进行攻击之类的攻击。这可以使用经过身份验证的模式(例如GCM或EAX),也可以使用加密 - 然后使用MAC方案。