Java 在Android和IPhone中使用AES-128进行加密(结果不同)
我试图在Android和IPhone平台上使用AES算法加密一些文本。我的问题是,即使使用相同的加密/解密算法(AES-128)和相同的固定变量(key、IV、mode),我在两个平台上也会得到不同的结果。我包括了来自两个平台的代码示例,用于测试加密/解密。如果能帮我确定我做错了什么,我将不胜感激Java 在Android和IPhone中使用AES-128进行加密(结果不同),java,android,iphone,encryption,aes,Java,Android,Iphone,Encryption,Aes,我试图在Android和IPhone平台上使用AES算法加密一些文本。我的问题是,即使使用相同的加密/解密算法(AES-128)和相同的固定变量(key、IV、mode),我在两个平台上也会得到不同的结果。我包括了来自两个平台的代码示例,用于测试加密/解密。如果能帮我确定我做错了什么,我将不胜感激 钥匙:“123456789abcdefg” 四:“1111111111” 纯文本:“HelloThere” 模式:“AES/CBC/NOP添加” Android代码: public class C
- 钥匙:“123456789abcdefg”
- 四:“1111111111”
- 纯文本:“HelloThere”
- 模式:“AES/CBC/NOP添加”
public class Crypto {
private final static String HEX = "0123456789ABCDEF";
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("CBC");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static String toHex(String txt) {
return toHex(txt.getBytes());
}
public static String fromHex(String hex) {
return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {
NSData* secretKey = [Cipher md5:cipherKey];
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
uint8_t iv[kCCBlockSizeAES128];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[secretKey bytes], kCCKeySizeAES128, iv, &cryptor);
if (status != kCCSuccess) {
return nil;
}
size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length], true);
void * buf = malloc(bufsize * sizeof(uint8_t));
memset(buf, 0x0, bufsize);
size_t bufused = 0;
size_t bytesTotal = 0;
status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
buf, bufsize, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
CCCryptorRelease(cryptor);
return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}
+ (NSData *) md5:(NSString *) stringToHash {
const char *src = [stringToHash UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(src, strlen(src), result);
return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
}
我的一些参考资料:
getBytes()
。这是一个错误,因为这意味着您使用的是默认字符集,而不是已知的字符集。改用getBytes(“UTF-8”)
,这样您就可以确切地知道将要获得的字节
我不知道Objective-C的等价物,但不要依赖默认值。将字符串转换为字节时显式指定UTF-8。这样,您将在两侧获得相同的字节
我还注意到,您在Objective-C代码中使用了MD5,但在Android代码中没有。这是故意的吗?难怪你会得到不同的结果 您的问题是您使用了一个SHA1PRNG进行密钥派生。此外,SHA1PRNG内部工作方式没有通用标准。AFAIR甚至J2SE和Bouncycaste实现都使用相同的种子输出不同的结果 因此,
getRawKey(byte[]seed)
的实现将生成一个随机键。如果使用该密钥进行加密,则会得到一个依赖于该密钥的结果。由于密钥是随机的,所以在iOS上不会得到相同的密钥,因此会得到不同的结果
如果您想要密钥派生函数,请使用PBKDF2之类的函数,该函数在密钥派生方面几乎完全标准化。请参阅我的答案,因为您实际上是在使用“种子”作为密码。(如果需要,只需将密钥长度256更改为128即可。)
试图通过将DRBG设定为具有相同值的种子来生成相同的密钥是不可靠的
接下来,您在Android加密中没有使用CBC或IV。我的例子也展示了如何正确地做到这一点。顺便说一下,您需要为加密的每封邮件生成一个新的IV,如我的示例所示,并将其与密文一起发送。否则,使用CBC是没有意义的。如果您想要Android和iPhone兼容代码的示例,请查看iOS和Java/Android的 这两个项目都是开源的,共享一种通用的数据格式。在这些库中,使用了AES 256位,但是如果需要修改代码以支持128位AES,这将是微不足道的 根据公认的答案,两个库都使用PBKDF2。对于我使用的iPhone,对于Android,使用以下代码:
public class AESCrypt {
private final Cipher cipher;
private final SecretKeySpec key;
private AlgorithmParameterSpec spec;
public AESCrypt(String password) throws Exception
{
// hash password with SHA-256 and crop the output to 128-bit for key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(password.getBytes("UTF-8"));
byte[] keyBytes = new byte[32];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
key = new SecretKeySpec(keyBytes, "AES");
spec = getIV();
}
public AlgorithmParameterSpec getIV()
{
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
IvParameterSpec ivParameterSpec;
ivParameterSpec = new IvParameterSpec(iv);
return ivParameterSpec;
}
public String encrypt(String plainText) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedText = new String(Base64.encode(encrypted, Base64.DEFAULT), "UTF-8");
return encryptedText;
}
public String decrypt(String cryptedText) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] bytes = Base64.decode(cryptedText, Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(bytes);
String decryptedText = new String(decrypted, "UTF-8");
return decryptedText;
}
}
注意:对于java中的android 我已经编写了这个管理器文件,它的功能对我来说非常好。这是AES 128,不含任何盐
public class CryptoManager {
private static CryptoManager shared;
private String privateKey = "your_private_key_here";
private String ivString = "your_iv_here";
private CryptoManager(){
}
public static CryptoManager getShared() {
if (shared != null ){
return shared;
}else{
shared = new CryptoManager();
return shared;
}
}
public String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(privateKey.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return android.util.Base64.encodeToString(encrypted, android.util.Base64.DEFAULT);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(privateKey.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = new byte[0];
original = cipher.doFinal(android.util.Base64.decode(encrypted, android.util.Base64.DEFAULT));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
您需要像这样调用函数
String dataToEncrypt = "I need to encrypt myself";
String encryptedData = CryptoManager.getShared().encrypt(data);
您将通过以下行获得加密字符串
String decryptedString = CryptoManager.getShared().decrypt(encryptedData);
+1-密钥位于名称psuedo随机数生成器中。不同的实现不会给出相同的结果,即使您使用相同的种子。首先,我想感谢您的反馈,但如果您能提供更多的细节或示例,我将非常感谢您的努力,因为这让我有点困惑,请参阅我关于getRawKey(..)实现的更新答案。没错。难怪!货运邪教节目最糟糕的时候。如果你想让任何程序工作,了解你在做什么会有帮助,而不是把你从互联网上复制的代码拼凑起来。你使用的是哪种
encrypt()
方法?你的问题一团糟。只提供必要的代码。您可以使用此解决方案,它适用于我:[在此处输入链接说明][1][1]:为什么您需要加密的数据相等?可能会无缘无故地降低代码的安全性。在大多数用例中,只有解密的数据必须相等。请参见crypto.stackexchange.com/q/5094@Chris这样做是为了将Android代码与AESCrypt ObjC iOS库对齐。添加AESCrypt ObjC不是我维护的。上面提到的Android代码和AESCrypt ObjC iOS代码与今天一样有效。谢谢