这让我很难过 – 以下代码使用SpongyCastle的Android加密/解密 – 我正在尝试为iOS实现跨平台加密/解密.
下面的代码(来自Android)使用PKCS7Padding处理AES 128bit CBC,使用提供的salt和密码,salt存储在MysqL数据库中,密码由最终用户提供,以下代码改编自此回答kelhoer.
我之所以使用AES128bit是因为AES256在iOS 4中不可用,它是在iOS5中引入的,并且不得不使用openssl来生成派生密钥和初始化向量(iv),因为学到苹果拒绝它是很冒险的与openssl库静态链接的应用程序.
由于该平台基于iOS 4.2,使用bundling and statically linking the 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,numRead,plainTemp,0);
final byte[] plain = new byte[offset];
System.arraycopy(plainTemp,plain,plain.length);
fout.write(plain,plain.length);
} else {
byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf,0);
int last = aesCipher.doFinal(plainTemp,offset);
final byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp,plain.length);
}
}
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void decrypt(InputStream fin,128);
aesCBC.init(false,new PKCS7Padding());
aesCipher.init(false,0);
// int last = aesCipher.doFinal(plainTemp,offset);
final byte[] plain = new byte[offset];
System.arraycopy(plainTemp,plain.length);
} else {
byte[] plainTemp = new byte[
aesCipher.getOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf,plain.length);
}
}
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
但是在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,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;
}
提供了数据和用户密码,并从CCCrypt获得返回码为-4304,表示解码不成功和错误.
我原以为编码方案可能会抛弃CommonCryptor的解密路由,因此转换为NSASCIIStringEncoding的方法很冗长.
Salt与密码数据一起存储,长度为32字节.
考虑到密码学方面的问题,我在这方面缺少什么.
实现也是直接复制,如发现here,密码,转换为PKCS12等效 – unicode,big-endian,最后填充两个额外的零.
Generator通过执行迭代次数生成派生密钥,在本例中为1000,如在Android端,使用SHA256摘要,最终生成的密钥和iv然后用作CCCryptorCreate的参数.
使用以下代码示例也不起作用,它在调用CCCryptorFinal时以-4304结束
代码摘录如下所示:
#define ITERATIONS 1000
PKCS12ParametersGenerator *pGen = [[PKCS12ParametersGenerator alloc]
init:argPassword
saltedHash:argPtrSalt
iterCount:ITERATIONS
keySize:128
initVectSize:128];
//
[pGen generateDerivedParameters];
//
CCCryptorRef decryptor = NULL;
// Create and Initialize the crypto reference.
CCCryptorStatus ccStatus = CCCryptorCreate(kCCDecrypt,pGen.derivedKey.bytes,kCCKeySizeAES128,pGen.derivedIV.bytes,&decryptor
);
NSAssert(ccStatus == kCCSuccess,@"Unable to initialise decryptor!");
//
size_t szPlainBufLen = cipherData.length + (kCCBlockSizeAES128);
// Calculate byte block alignment for all calls through to and including final.
size_t szPtrPlainBufSize = CCCryptorGetOutputLength(decryptor,true);
uint8_t *ptrPlainBuf = calloc(szPtrPlainBufSize,sizeof(uint8_t));
//
// Set up initial size.
size_t remainingBytes = szPtrPlainBufSize;
uint8_t *ptr = ptrPlainBuf;
size_t movedBytes = 0;
size_t totalBytesWritten = 0;
// Actually perform the encryption or decryption.
ccStatus = CCCryptorUpdate(decryptor,(const void *) cipherData.bytes,szPtrPlainBufSize,ptr,remainingBytes,&movedBytes
);
NSAssert(ccStatus == kCCSuccess,@"Unable to update decryptor! Error: %d",ccStatus);
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
//
// Finalize everything to the output buffer.
CCCryptorStatus resultCCStatus = CCCryptorFinal(decryptor,&movedBytes
);
totalBytesWritten += movedBytes;
if(decryptor) {
(void) CCCryptorRelease(decryptor);
decryptor = NULL;
}
NSAssert(resultCCStatus == kCCSuccess,resultCCStatus);
有趣的是,解密工作,对CCCryptorFinal的最后调用返回0,如果我在CCCryptorCreate的开头用kCCOptionPKCS7Padding代替0x0000,即没有填充.唉,这些数据并不是我所期望的,无论什么时候“不起作用”,它仍然是完全混乱的.
它在某个地方失败了,所以如果有人对如何实现等价物有任何更好的想法,我会很高兴听到其他意见.
要么改变Android方面的机制,使其与iPhone“跨平台”兼容,要么寻求替代的加密解决方案,在两端兼容,代价是用于使数据交换便携的平台两侧的加密能力较弱.
提供的输入数据:
> Base64编码密码,盐和密码用’:’分隔:tnNhKyJ2vvrUzAmtQV5q9uEwzzAH63sTKtLf4pOQylw =:qTBluA aNeFnEUfkUFUEVgNYrdz7enn5W1n4Q9uBKYmFfJeSCcbsfziErsa4EU9Cz / pO0KE4WE1QdqRcvSXthQ ==
>提供的密码是f00b4r
>最初的字符串是快速的棕色狐狸跳过懒狗并跑开了