安卓AES 128加密

6
我将尝试在Android上实现AES128加密。我已经使用Objective C在iPhone上找到了解决方案,但是在移植到Android上时出现了问题。我在stackoverflow上搜索了解决方案,但貌似我做错了什么。考虑到数据和字符串转换,我的Java水平相对较弱,因此可能会遗漏一些重要内容。
这是我的iPhone加密代码:
char keyPtr[kCCKeySizeAES128+1];
[keyString getCString:keyPtr
            maxLength:sizeof(keyPtr)
             encoding:NSASCIIStringEncoding];

// CString for the plain text
char plainBytes[[plainString length]+1];
[plainString getCString:plainBytes
              maxLength:sizeof(plainBytes)
               encoding:NSASCIIStringEncoding];

size_t bytesEncrypted = 0;

// Allocate the space for encrypted data
NSUInteger dataLength = [plainString length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void* buffer = malloc(bufferSize);

// Encrypt
CCCryptorStatus ret = CCCrypt(kCCEncrypt,
                              kCCAlgorithmAES128,
                              kCCOptionPKCS7Padding | kCCOptionECBMode,
                              keyPtr,
                              kCCKeySizeAES128,
                              NULL,
                              plainBytes, sizeof(plainBytes),
                              buffer, bufferSize,
                              &bytesEncrypted);
if (ret != kCCSuccess) {
    free(buffer);
}

encryptedData = [NSData dataWithBytes:buffer length:bytesEncrypted];

这是我的Java代码:

    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));

在iPhone和Java中使用相同的密钥和明文会得到不同的结果。我的iPhone结果符合我的需求,所以我正在尝试让Java给我iPhone的结果。我确定在Java中缺少某些东西,只是不确定是什么。

编辑

根据下面的建议,我修改了我的Java代码如下:

    byte[] keyBytes = plainTextKey.getBytes("US-ASCII");
    SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(plainText.getBytes("US-ASCII"));

但我仍然在Android和iPhone之间获取不同的结果。


3
您的明文编码不同。Android 使用UTF8,iPhone使用ASCII。 - vcsjones
1
作为一则附注,我不建议使用 ECB 作为您的密码模式(除非您试图与已经使用它的系统保持兼容)。这使得很容易识别加密数据中的模式。 - vcsjones
谢谢vcsjones,我已经更新了我的代码为plainText.getBytes("ASCII"),但结果仍然不同。 - Pabs
2
正如vcsjones所指出的那样,ECB非常不安全(在许多情况下,它几乎不能算作加密)。您还在错误地生成密钥。您不能只是将ASCII字节复制到AES密钥中。这会丢失几乎所有的密钥空间,并使其易于暴力破解。您需要使用密钥派生函数(通常为PBKDF2)将字符串转换为密钥。如果您需要一个在iOS和Android上运行的开箱即用的加密解决方案,请参见iOS的RNCryptor和Java的JNCryptor。它们编写相同的格式。 - Rob Napier
3个回答

4
除了纯文本编码困难(如vcsjones在评论中指出的)之外,还要确保密钥字符串的编码相同(请注意,直接使用原始字符串(如密码)作为加密密钥是不好的做法,请使用像PBKDF2这样的密钥派生函数来从密码中获取一个密钥)。
此外,Java对于ASCII的编码字符串是"US-ASCII",而不仅仅是"ASCII",因此请确保在您的"getBytes"调用中使用它。
编辑:找到了你的问题:iOS字符串被加密时末尾多了一个空字符(0x00),而Java没有。因此,在Java中加密"hello world\0"将会给你与iOS中加密"hello world"相同的输出结果。

谢谢你的建议,我完全忽略了“US-ASCII”。不幸的是问题仍然存在。我理解EBC的问题,但在这种特殊情况下我需要使用它。我已经更新了代码,使用了“US-ASCII”作为明文和密钥,但结果仍然相同。 - Pabs

0

互联网上的大多数AES示例都是弱实现。要使实现强大,应始终使用随机IV并对密钥进行哈希处理。

要获得更安全(随机IV + 哈希密钥)的跨平台(Android、iOS、C#)AES实现,请参见我的答案-https://dev59.com/UXTYa4cB1Zd3GeqPsDmq#24561148


0

我已经编写了这个管理文件,它的功能对我来说完美地运作。这是针对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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接