我正在尝试在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日--------- --------------------------- 为了避免混淆,我提供了一些样本和我的进展。我更改了一些保密的数据,但长度和格式保持不变。为节省时间,我只透露核心功能。没有对代码的评论,因为我认为代码是自我解释的。
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;
---------------------------------------- 9月13日更新---- ------------------------- 我在iOS示例代码中更改了_key,手动将前16个字节作为新密钥,其他部分保持不变,并且正在运行!到目前为止,我解决了密钥长度问题。
但Android版本输出与iOS版本不同,用于一些长文本,如30或40字节。我的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中。
答案 0 :(得分:5)
剩下的差异(假设您只使用了密钥的前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中获得了完全相同的结果。
答案 1 :(得分:1)
没有36字节(288位)AES密钥。 AES 256将使用32字节密钥,因此可能是您拥有的,具有一些额外的标头/尾部字节。你从哪里得到这把钥匙?格式是什么? Apple实现可能会丢弃不需要的字节,或者它已经知道您正在使用的特殊格式。
答案 2 :(得分:1)
36个字节实际上是密码吗?如果是这样,那么使用的密钥很可能是SHA-256(密码)或SHA-512(密码)。
ETA:
重新更新。我注意到您的代码使用的是ECB模式。那是不安全的。很可能Apple正在使用CBC模式,因此您难以解密更长(超过16个字节)的消息。尝试将模式更改为CBC,并使用16个字节的神秘输入作为IV。快速查看CommonCryptor.c的Apple代码,它们似乎正在使用PKCS7填充,因此您也应该使用它。
答案 3 :(得分:0)
如果您想通过网络应用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;
}