【问题标题】:AES 128 Implementation with predefined key使用预定义密钥的 AES 128 实现
【发布时间】:2011-09-12 01:17:56
【问题描述】:

我正在尝试在 Android 上实现 AES128 算法,并且我参考了此链接以了解基本的 AES 实现 (http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html)。

问题是,对于我的项目,密钥是预定义的,它是 36 字节,而不是 16/24/32 字节。所以我总是遇到“密钥长度不是 128/194/256 位”异常。我尝试了来自 iphone sdk 的解决方案(请参阅此链接:The iOS encryption framework),即使我传递了 36 字节的预定义密钥,它也能正常工作。由于我找不到 Apple 发布的 BlockCipher.c/CommonCryptor.c 的实现细节,谁能帮我弄清楚他们是如何从 36 字节中选择 16 字节的?

谢谢。

-----------------------9 月 13 日更新--------- -------------------------- 为了避免混淆,我提供了一些示例和我的进度。我更改了一些机密数据,但长度和格式保持不变。为了节省时间,我只透露核心功能。代码没有 cmets,因为我认为代码已经足够自我解释了。

iOS 示例:

NSString * _key = @"some 36 byte key";
StringEncryption *crypto = [[[StringEncryption alloc] init] autorelease]; 
NSData *_inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding]; 
CCOptions padding = kCCOptionPKCS7Padding;
NSData *encryptedData = [crypto encrypt:_inputData key:[_key dataUsingEncoding:NSUTF8StringEncoding] padding:&padding];
NSString *encryptedString = [encryptedData base64EncodingWithLineLength:0];
return encryptedString;

[crypto encrypt] 实现与我上面提到的链接完全相同。它以加密模式调用 doCipher。核心函数包括 CCCryptorCreate、CCCryptorUpdate 和 CCCryptorFinal,它们来自 . CCCryptorCreate 处理密钥长度。它传递原始密钥字节,并传递一个整数 16 (kCCKeySizeAES128) 作为密钥大小并执行此操作。调用层次结构类似于 CCCryptorCreate/CommonCryptor.c => ccBlockCipherCallouts->CCBlockCipherInit/BlockCipher.c => ccAlgInfo->setkey/BlockCipher.c 。 setkey 实际上是一个指向函数的指针,对于 AES 它指向 aes_cc_set_key。而且我找不到 aes_cc_set_key 实现,在这里迷路了。

----------------------------------------9 月 13 日更新 ---- ------------------------- 我更改了iOS示例代码中的_key,手动将前16个字节作为新密钥,其他部分保持不变,并且可以正常工作!!!至此,我解决了密钥长度问题。

但是对于一些长的纯文本,例如 30 或 40 字节,Android 版本的输出与 iOS 版本不同。我的java实现如下:

String key = "some 16 byte key";
byte[] keyBytes = key.getBytes("UTF-8");
byte[] plainBytes = plainText.getBytes("UTF-8");
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(plainBytes);
String result = Base64.encodeBytes(encrypted);
return result;

Base64 来自 org.apache.commons.codec.binary.Base64。问题是什么?或者关于可以做同样事情的 c/c++ 库的任何提示?我也可以将它导入到android中。

【问题讨论】:

  • 您的加密是否需要产生与在其他系统上实现相同的结果?如果是,那么您将需要有关该信息的更多信息,以便您可以将密钥减少到 16 个字节。 (iPhone 是否产生与参考实现相同的结果?)如果没有,那么您可以自己减少密钥,例如通过将前 16 个字节与接下来的 16 个字节和最后 4 个字节进行异或(用 0 扩展为 16 个字节)。
  • 其实这个app是有iOS版本的,我需要在iOS版本的基础上实现android版本。是的,我的实现需要产生与 iOS 实现相同的结果。由于它是客户端-服务器结构,所以其他人实现了服务器部分,包括解密部分,并提供了接口。 iOS 版本完美运行,我测试了服务器返回 HeaderDecryption Error 除非 android 版本输出与指定纯文本的 iOS 输出相同的密文。我将更新问题以使其更详细。
  • 看看这个问题以及我和 iOS 的答案:stackoverflow.com/questions/17535918/…

标签: android encryption


【解决方案1】:

剩下的区别(假设您只使用了密钥的前 16 个字节)是密码流模式。 iOS 代码使用初始化设置为全零的 CBC 模式。然而,Android 代码使用 ECB。

所以正确的 Java/Android 代码是:

// convert key to bytes
byte[] keyBytes = key.getBytes("UTF-8");
// Use the first 16 bytes (or even less if key is shorter)
byte[] keyBytes16 = new byte[16];
System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, 16));

// convert plain text to bytes
byte[] plainBytes = plainText.getBytes("UTF-8");

// setup cipher
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16]; // initialization vector with all 0
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));

// encrypt
byte[] encrypted = cipher.doFinal(plainBytes);

我用大约 100 字节的数据对其进行了测试,在 iOS 和 Java 上得到了完全相同的结果。

【讨论】:

  • 完全正确!!!!当我探索 iOS 源代码时,我发现了同样的事实。根据 Rossum 的建议,我注意到我的模式不正确。今天我正在阅读 JEC 文档并考虑如何修改它。这个答案绝对是我想要的,并且我测试它连接服务器,它工作!谢谢科多!!!!!!
【解决方案2】:

没有 36 字节(288 位)的 AES 密钥。 AES 256 将使用 32 字节的密钥,所以也许这就是你所拥有的,还有一些额外的标题/尾随字节。你从哪里弄来这把钥匙的?格式是什么? Apple 实现可能会丢弃不需要的字节,或者它已经知道您正在使用的特殊格式。

【讨论】:

  • 我了解AES可接受的密钥长度只有128/192/256,算法实际上是AES128。我从从事服务器部分(实际上是解密部分)的团队那里获得了密钥,而服务器部分是用 C# 实现的。纯文本和密钥的格式都是 UTF-8 字节。是的,Apple 实现必须对额外的字节做了一些事情,对于这个问题,我只想知道他们如何处理额外的字节。我将更新问题以使其更详细。
【解决方案3】:

这 36 个字节实际上是密码吗?如果是这样,那么所使用的密钥很可能是 SHA-256(密码)或 SHA-512(密码)。

预计到达时间:
重新更新。我注意到您的代码正在使用 ECB 模式。那是不安全的。很可能 Apple 正在使用 CBC 模式,因此您难以解密更长(超过 16 个字节)的消息。尝试将模式更改为 CBC,并使用多 16 个字节的神秘输入作为 IV。快速查看CommonCryptor.c 的 Apple 代码,它们似乎在使用 PKCS7 填充,因此您也应该使用它。

【讨论】:

  • 没有。这只是密码。我会更新问题以揭示更多细节。
  • 您需要查看 Apple 方面的详细文档,以确定它从 36 字节获取 16 字节的确切操作。
【解决方案4】:

如果您想应用 base64 编码通过网络传输,这是正确的代码:

public String encryptString(String string, String key)
{
    byte[] aesData;
    String base64="";

    try 
    {
        aesData = encrypt(key, string.getBytes("UTF8"));
        base64 = Base64.encodeToString(aesData, Base64.DEFAULT);
    } 
    catch (Exception e) 
    {
        e.printStackTrace();
    }       

    return base64;
}

public String decryptString(String string, String key)
{
    byte[] debase64 = null;
    String result="";

    try 
    {
        debase64=Base64.decode(string, Base64.DEFAULT);
        byte[] aesDecrypted = decrypt(key, debase64);;

        result = new String(aesDecrypted, "UTF8");
    } 
    catch (Exception e) 
    {
        e.printStackTrace();
    }       

    return result;
}

private byte[] decrypt(String k, byte[] plainBytes) throws Exception 
{
    // convert key to bytes
    byte[] keyBytes = k.getBytes("UTF-8");
    // Use the first 16 bytes (or even less if key is shorter)
    byte[] keyBytes16 = new byte[16];
    System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, 16));

    // setup cipher
    SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] iv = new byte[16]; // initialization vector with all 0
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv));

    // encrypt
    byte[] encrypted = cipher.doFinal(plainBytes);

    return encrypted;
}

private byte[] encrypt(String k, byte[] plainBytes) throws Exception 
{
    // convert key to bytes
    byte[] keyBytes = k.getBytes("UTF-8");
    // Use the first 16 bytes (or even less if key is shorter)
    byte[] keyBytes16 = new byte[16];
    System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, 16));

    // setup cipher
    SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] iv = new byte[16]; // initialization vector with all 0
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));

    // encrypt
    byte[] encrypted = cipher.doFinal(plainBytes);

    return encrypted;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多