t0m*_*13b 5 android ios commoncrypto spongycastle
这让我很难过 - 以下代码使用SpongyCastle的Android加密/解密 - 我正在尝试为iOS实现跨平台加密/解密.
下面的代码(来自Android)使用PKCS7Padding处理AES 128bit CBC,使用提供的salt和密码,salt存储在mysql数据库中,密码由最终用户提供,以下代码改编自此kelhoer回答.
我之所以使用AES128bit是因为AES256在iOS 4+中不可用,它是在iOS5 +中引入的,并且不得不使用脚趾openssl来生成派生密钥和初始化矢量(iv),因为学到苹果公司拒绝它是很冒险的与openssl库静态链接的应用程序.
由于该平台基于iOS 4.2+,因此使用捆绑和静态链接openssl库似乎过度杀死并且最好使用CommonCryptor库.
这是使用Spongycastle代码的Android版本:
private static void encrypt(InputStream fin,
OutputStream fout,
String password,
byte[] bSalt) {
try {
PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(
new SHA256Digest()
);
char[] passwordChars = password.toCharArray();
final byte[] pkcs12PasswordBytes =
PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
pGen.init(pkcs12PasswordBytes, bSalt, ITERATIONS);
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
ParametersWithIV aesCBCParams =
(ParametersWithIV) pGen.generateDerivedParameters(128, 128);
aesCBC.init(true, aesCBCParams);
PaddedBufferedBlockCipher aesCipher =
new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
aesCipher.init(true, aesCBCParams);
byte[] buf = new byte[BUF_SIZE];
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = fin.read(buf)) >= 0) {
if (numRead == 1024) {
byte[] plainTemp = new byte[
aesCipher.getUpdateOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
final byte[] plain = new byte[offset];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
} else {
byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
}
}
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void decrypt(InputStream fin,
OutputStream fout,
String password,
byte[] bSalt) {
try {
PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(
new SHA256Digest()
);
char[] passwordChars = password.toCharArray();
final byte[] pkcs12PasswordBytes =
PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
pGen.init(pkcs12PasswordBytes, bSalt, ITERATIONS);
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
ParametersWithIV aesCBCParams =
(ParametersWithIV) pGen.generateDerivedParameters(128, 128);
aesCBC.init(false, aesCBCParams);
PaddedBufferedBlockCipher aesCipher =
new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
aesCipher.init(false, aesCBCParams);
byte[] buf = new byte[BUF_SIZE];
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = fin.read(buf)) >= 0) {
if (numRead == 1024) {
byte[] plainTemp = new byte[
aesCipher.getUpdateOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
// int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
} else {
byte[] plainTemp = new byte[
aesCipher.getOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
}
}
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
但是在iOS 4.2(使用XCode)下我无法弄清楚如何做等效的,
这就是我在Objective C下尝试过的,其目标是解密来自Android端的数据,存储在mysql数据库中,以测试它:
+(NSData*) decrypt:(NSData*)cipherData
userPassword:(NSString*)argPassword
genSalt:(NSData*)argPtrSalt{
size_t szPlainBufLen = cipherData.length + (kCCBlockSizeAES128);
uint8_t *ptrPlainBuf = malloc(szPlainBufLen);
//
const unsigned char *ptrPasswd =
(const unsigned char*)[argPassword
cStringUsingEncoding:NSASCIIStringEncoding];
int ptrPasswdLen = strlen(ptrPasswd);
//
NSString *ptrSaltStr = [[NSString alloc]
initWithData:argPtrSalt
encoding:NSASCIIStringEncoding];
const unsigned char *ptrSalt =
(const unsigned char *)[ptrSaltStr UTF8String];
NSString *ptrCipherStr =
[[NSString alloc]initWithData:cipherData
encoding:NSASCIIStringEncoding];
unsigned char *ptrCipher = (unsigned char *)[ptrCipherStr UTF8String];
unsigned char key[kCCKeySizeAES128];
unsigned char iv[kCCKeySizeAES128];
//
//int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md,
//const unsigned char *salt, const unsigned char *data,
//int datal, int count, unsigned char *key,unsigned char *iv);
int i = EVP_BytesToKey(EVP_aes_128_cbc(),
EVP_sha256(),
ptrSalt,
ptrPasswd,
ptrPasswdLen,
PBKDF2_ITERATIONS,
key,
iv);
NSAssert(i == kCCKeySizeAES128,
@"Unable to generate key for AES");
//
size_t cipherLen = [cipherData length];
size_t outlength = 0;
//
CCCryptorStatus resultCCStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key,
kCCBlockSizeAES128,
iv,
ptrCipher,
cipherLen,
ptrPlainBuf,
szPlainBufLen,
&outlength);
NSAssert(resultCCStatus == kCCSuccess,
@"Unable to perform PBE AES128bit decryption: %d", errno);
NSData *ns_dta_PlainData = nil;
if (resultCCStatus == kCCSuccess){
ns_dta_PlainData =
[NSData dataWithBytesNoCopy:ptrPlainBuf length:outlength];
}else{
return nil;
}
return ns_dta_PlainData;
}
Run Code Online (Sandbox Code Playgroud)
已经提供的数据和用户的密码,并得到一个返回代码CCCrypt为-4304这表明尚未成功,坏的解码.
我原以为编码方案可能会抛弃CommonCryptor的解密路由,因此转换为啰嗦的方式NSASCIIStringEncoding.
Salt与密码数据一起存储,长度为32字节.
考虑到密码学方面的问题,我在这方面缺少什么.
是的,我不得不放弃Android端的加密算法,这对我来说是一个挑战,找到一个跨平台兼容的算法。
我读了很多关于Rob Napier 的 RNCryptor 的文章,在谷歌搜索了 Android 的等效版本后,我发现了JNCryptor,我冒险在 iOS 端使用了 RNCryptor 。
在github上分叉 JNCryptor 代码,以添加增强功能,能够指定自定义设置,并为旧版本的 Android 使用 SpongyCastle。从那时起,两个平台都能够互换加密/解密。
我增强 JNCryptor 的原因是 PKDBF2 函数的迭代计数非常高 - 10,000 并且是默认值(因为代码将在旧手机上运行 - 它卡住了 - 如果你有双核/四核,那就太棒了!),并且需要重写迭代计数以使其更“可承受” - 1,000。RNCryptor 可以使用自定义设置。
感谢 Rob Napier 和 Duncan Jones 的工作!
| 归档时间: |
|
| 查看次数: |
1554 次 |
| 最近记录: |