【问题标题】:Android AES 128 encryptionAndroid AES 128 加密
【发布时间】:2013-03-13 16:06:30
【问题描述】:

我正在尝试在 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 之间得到不同的结果

【问题讨论】:

  • 您的明文编码不同。 android 使用 UTF8,iPhone 使用 ASCII。
  • 附带说明,我不鼓励将 ECB 用于您的密码模式(除非您试图保持与已经使用它的系统的兼容性)。它使识别加密数据中的模式变得相当容易。
  • 感谢 vcsjones,我已将代码更新为 plainText.getBytes("ASCII") 但结果仍然不同
  • 正如 vcsjones 所指出的,ECB 非常不安全(在许多情况下,它几乎不能算作加密)。您还错误地生成了密钥。您不能只将 ASCII 字节复制到 AES 密钥中。这丢弃了几乎所有的键空间,并且很容易暴力破解。您想使用密钥派生函数(通常为 PBKDF2)将字符串转换为密钥。如果您需要在 iOS 和 Android 上运行的开箱即用加密解决方案,请参阅适用于 iOS 的 RNCryptor 和适用于 Java 的 JNCryptor。他们写的格式相同。

标签: android iphone encryption aes


【解决方案1】:

除了纯文本的编码困难(正如 vcsjones 在 cmets 中指出的那样),请确保密钥字符串的编码相同(请注意,使用原始字符串,如密码,直接作为加密密钥是个坏消息,请在密码上使用 PBKDF2 之类的密钥派生函数来获取密钥)。

此外,Java 的 ASCII 编码字符串是 US-ASCII,而不仅仅是 ASCII,因此请确保在您的 getBytes 调用中使用它。

编辑:发现您的问题:iOS 字符串在末尾使用额外的空字符 (0x00) 进行加密,而 java 没有。因此,在 java 中加密“hello world\0”会得到与 iOS 中“hello world”相同的输出

【讨论】:

  • 感谢您的提示,我完全错过了“US-ASCII”。不幸的是,问题仍然是一样的。我了解 EBC 的问题,但在这种特殊情况下,我需要使用它。我还更新了代码以使用“US-ASCII”明文和密钥,但结果仍然相同。
【解决方案2】:

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

如需更安全(随机 IV + 散列密钥)跨平台(android、ios、c#)实现 AES,请在此处查看我的答案 - https://stackoverflow.com/a/24561148/2480840

【讨论】:

    【解决方案3】:

    我已经编写了这个管理器文件,它的功能对我来说非常好用。这是针对 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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      • 1970-01-01
      • 2018-01-08
      • 2015-03-10
      • 2019-08-17
      • 2020-04-15
      • 1970-01-01
      相关资源
      最近更新 更多