这让我感到困惑 - 以下代码使用了SpongyCastle的加密/解密来实现Android - 我正在尝试为iOS实现跨平台的加密/解密。以下代码(来自Android)可行,使用提供的盐和密码,采用AES 128位CBC与PKCS7Padding,其中盐存储在mysql数据库中,密码由最终用户输入,以下代码改编自kelhoer 的这个答案。
我使用AES128bit的原因是iOS 4+中不支持AES256,它是在iOS5+中引入的,而且必须涉足使用
我使用AES128bit的原因是iOS 4+中不支持AES256,它是在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();
}
}
然而,在 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;
}
我已经提供了数据和用户密码,并从CCCrypt
获得了返回代码,其值为-4304
,这表明解码失败。
我认为可能是编码方案导致CommonCryptor的解密路由出现问题,因此才需要通过冗长的方式转换为NSASCIIStringEncoding
。
盐值存储在密文数据中,长度为32字节。
在这方面我错过了什么,需要注意的是,我对加密学很弱。