iOS中的PBEWithMD5AndDESencryption

我在iOS中遇到PBEWithMD5AndDESencryption问题。 我有我的string使用此encryption和解密, https://gist.github.com/788840/24bc73ecd0ac3134cbd242892c74a06ac561d37b 。

问题是我得到不同的encryption值取决于我的方法在哪个类。例如,我将所有的encryption方法移动到一个帮助类,并运行它。 我注意到我得到了一个不同的encryption值。

我现在在不同的类中有相同的方法的两个相同的版本,我并排运行它们。 他们获得不同的encryption值,而且不能解密其他人的值。 我有点沉迷于此。

这是encryption/解密的助手类。

@implementation CryptoHelper #pragma mark - #pragma mark Init Methods - (id)init { if(self = [super init]) { } return self; } #pragma mark - #pragma mark String Specific Methods /** * Encrypts a string for social blast service. * * @param plainString The string to encrypt; * * @return NSString The encrypted string. */ - (NSString *)encryptString: (NSString *) plainString{ // Convert string to data and encrypt NSData *data = [self encryptPBEWithMD5AndDESData:[plainString dataUsingEncoding:NSUTF8StringEncoding] password:@"1111"]; // Get encrypted string from data return [data base64EncodingWithLineLength:1024]; } /** * Descrypts a string from social blast service. * * @param plainString The string to decrypt; * * @return NSString The decrypted string. */ - (NSString *)decryptString: (NSString *) encryptedString{ // decrypt the data NSData * data = [self decryptPBEWithMD5AndDESData:[NSData dataWithBase64EncodedString:encryptedString] password:@"1111"]; // extract and return string return [NSString stringWithUTF8String:[data bytes]]; } #pragma mark - #pragma mark Crypto Methods - (NSData *)encryptPBEWithMD5AndDESData:(NSData *)inData password:(NSString *)password { return [self encodePBEWithMD5AndDESData:inData password:password direction:1]; } - (NSData *)decryptPBEWithMD5AndDESData:(NSData *)inData password:(NSString *)password { return [self encodePBEWithMD5AndDESData:inData password:password direction:0]; } - (NSData *)encodePBEWithMD5AndDESData:(NSData *)inData password:(NSString *)password direction:(int)direction { NSLog(@"helper data = %@", inData); static const char gSalt[] = { (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA }; unsigned char *salt = (unsigned char *)gSalt; int saltLen = strlen(gSalt); int iterations = 15; EVP_CIPHER_CTX cipherCtx; unsigned char *mResults; // allocated storage of results int mResultsLen = 0; const char *cPassword = [password UTF8String]; unsigned char *mData = (unsigned char *)[inData bytes]; int mDataLen = [inData length]; SSLeay_add_all_algorithms(); /*X509_ALGOR *algorithm = PKCS5_pbe_set(NID_pbeWithMD5AndDES_CBC, iterations, salt, saltLen);*/ const EVP_CIPHER *cipher = EVP_des_cbc(); // Need to set with iv X509_ALGOR *algorithm = PKCS5_pbe2_set_iv(cipher, iterations, salt, saltLen, salt, NID_hmacWithMD5); memset(&cipherCtx, 0, sizeof(cipherCtx)); if (algorithm != NULL) { EVP_CIPHER_CTX_init(&(cipherCtx)); if (EVP_PBE_CipherInit(algorithm->algorithm, cPassword, strlen(cPassword), algorithm->parameter, &(cipherCtx), direction)) { EVP_CIPHER_CTX_set_padding(&cipherCtx, 1); int blockSize = EVP_CIPHER_CTX_block_size(&cipherCtx); int allocLen = mDataLen + blockSize + 1; // plus 1 for null terminator on decrypt mResults = (unsigned char *)OPENSSL_malloc(allocLen); unsigned char *in_bytes = mData; int inLen = mDataLen; unsigned char *out_bytes = mResults; int outLen = 0; int outLenPart1 = 0; if (EVP_CipherUpdate(&(cipherCtx), out_bytes, &outLenPart1, in_bytes, inLen)) { out_bytes += outLenPart1; int outLenPart2 = 0; if (EVP_CipherFinal(&(cipherCtx), out_bytes, &outLenPart2)) { outLen += outLenPart1 + outLenPart2; mResults[outLen] = 0; mResultsLen = outLen; } } else { unsigned long err = ERR_get_error(); ERR_load_crypto_strings(); ERR_load_ERR_strings(); char errbuff[256]; errbuff[0] = 0; ERR_error_string_n(err, errbuff, sizeof(errbuff)); NSLog(@"OpenSLL ERROR:\n\tlib:%s\n\tfunction:%s\n\treason:%s\n", ERR_lib_error_string(err), ERR_func_error_string(err), ERR_reason_error_string(err)); ERR_free_strings(); } NSData *encryptedData = [NSData dataWithBytes:mResults length:mResultsLen]; //(NSData *)encr_buf; //NSLog(@"encryption result: %@\n", [encryptedData base64EncodingWithLineLength:1024]); EVP_cleanup(); return encryptedData; } } EVP_cleanup(); return nil; } @end 

我试图复制这个Java函数的结果。 我有相同的盐。

 public DesEncrypter(String passPhrase) { try { // Create the key KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount); SecretKey key = SecretKeyFactory.getInstance( "PBEWithMD5AndDES").generateSecret(keySpec); ecipher = Cipher.getInstance(key.getAlgorithm()); dcipher = Cipher.getInstance(key.getAlgorithm()); // Prepare the parameter to the ciphers AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount); // Create the ciphers ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec); } catch (java.security.InvalidAlgorithmParameterException e) { } catch (java.security.spec.InvalidKeySpecException e) { } catch (javax.crypto.NoSuchPaddingException e) { } catch (java.security.NoSuchAlgorithmException e) { } catch (java.security.InvalidKeyException e) { } } 

接受的答案似乎使用OpenSSL,这是不包含在iOS SDK中。 这是一个使用包含的CommonCrypto库(在libSystem中)的encryption和解密解决scheme。 我有一点ObjC n00b,所以拿一点盐的代码。 顺便说一句,这段代码将成功解密使用Java的PBEWithMD5AndDES密码encryption的数据。 希望这会为别人节省一两天的时间。

 #include <CommonCrypto/CommonDigest.h> #include <CommonCrypto/CommonCryptor.h> +(NSData*) cryptPBEWithMD5AndDES:(CCOperation)op usingData:(NSData*)data withPassword:(NSString*)password andSalt:(NSData*)salt andIterating:(int)numIterations { unsigned char md5[CC_MD5_DIGEST_LENGTH]; memset(md5, 0, CC_MD5_DIGEST_LENGTH); NSData* passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; CC_MD5_CTX ctx; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, [passwordData bytes], [passwordData length]); CC_MD5_Update(&ctx, [salt bytes], [salt length]); CC_MD5_Final(md5, &ctx); for (int i=1; i<numIterations; i++) { CC_MD5(md5, CC_MD5_DIGEST_LENGTH, md5); } size_t cryptoResultDataBufferSize = [data length] + kCCBlockSizeDES; unsigned char cryptoResultDataBuffer[cryptoResultDataBufferSize]; size_t dataMoved = 0; unsigned char iv[kCCBlockSizeDES]; memcpy(iv, md5 + (CC_MD5_DIGEST_LENGTH/2), sizeof(iv)); //iv is the second half of the MD5 from building the key CCCryptorStatus status = CCCrypt(op, kCCAlgorithmDES, kCCOptionPKCS7Padding, md5, (CC_MD5_DIGEST_LENGTH/2), iv, [data bytes], [data length], cryptoResultDataBuffer, cryptoResultDataBufferSize, &dataMoved); if(0 == status) { return [NSData dataWithBytes:cryptoResultDataBuffer length:dataMoved]; } else { return NULL; } } 

问题是你没有为你的encryption指定一个IV,这样一个随机数就会自动生成,这也是你每次得到不同结果的原因。

尝试使用PKCS5_pbe2_set_iv而不是PKCS5_pbe2_set提供一个明确的IV值,您可以随意select,就像您的salt值一样。

使用johwayner的回复,我解决了这个问题,以解密来自jasypt的 Java的PBEWithMD5AndDES 。 以下是我最终的结果:

 @interface NSData (PBEEncryption) /** * Decrypt the receiver using PKCS#5 PBE with MD5 and DES assuming that the salt is prefixed into the first 8 bytes of * the data and the number of key obtention iterations is 1000. This is compatible with Java's PBEWithMD5AndDES * encryption when the encryption generates a random salt for the data (the default when providing no salt). */ - (NSData *)decrytpPBEWithMD5AndDESUsingPassword:(NSData *)password; /** * Decrypt the receiver using PKCS#5 PBE with MD5 and DES. Explicitly provide the salt and number of key obtention * iterations. */ - (NSData *)decrytpPBEWithMD5AndDESUsingPassword:(NSData *)password salt:(NSData *)salt iterations:(NSUInteger)iterations; @end @implementation NSData (PBEEncryption) - (NSData *)decrytpPBEWithMD5AndDESUsingPassword:(NSData *)password { NSData *salt = nil; NSData *input = self; if ([input length] > 8) { salt = [input subdataWithRange:NSMakeRange(0, 8)]; input = [input subdataWithRange:NSMakeRange(8, [input length] - 8)]; } return [input decrytpPBEWithMD5AndDESUsingPassword:password salt:salt iterations:1000]; } - (NSData *)decrytpPBEWithMD5AndDESUsingPassword:(NSData *)password salt:(NSData *)salt iterations:(NSUInteger)iterations { unsigned char md5[CC_MD5_DIGEST_LENGTH] = {}; CC_MD5_CTX ctx; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, [password bytes], [password length]); CC_MD5_Update(&ctx, [salt bytes], [salt length]); CC_MD5_Final(md5, &ctx); for (NSUInteger i = 1; i < iterations; i++) { CC_MD5(md5, CC_MD5_DIGEST_LENGTH, md5); } // initialization vector is the second half of the MD5 from building the key unsigned char iv[kCCBlockSizeDES]; assert(kCCBlockSizeDES == CC_MD5_DIGEST_LENGTH / 2); memcpy(iv, md5 + kCCBlockSizeDES, sizeof(iv)); NSMutableData *output = [NSMutableData dataWithLength:([self length] + kCCBlockSize3DES)]; size_t outputLength = 0; CCCryptorStatus status = CCCrypt(kCCDecrypt, kCCAlgorithmDES, kCCOptionPKCS7Padding, md5, kCCBlockSizeDES, iv, [self bytes], [self length], [output mutableBytes], [output length], &outputLength); if (status == kCCSuccess) { [output setLength:outputLength]; } else { output = nil; } return output; } @end 

像这样使用:

 NSString *password = @"myTopSecretPassword"; NSData *inputData = [NSData dataFromBase64String:@"base64string"]; NSData *outputData = [inputData decrytpPBEWithMD5AndDESUsingPassword: [password dataUsingEncoding:NSUTF8StringEncoding]]; 

不知道该协议是在这里接受答案/ upvoting他们。 我道歉,如果我做错了。 答案结果是盐中没有最后一个字节。 我实际上并不需要3DESencryption。 我提出了另一个答案,因为它有助于更​​多地了解encryption。

这是最后的目标C类。

 @implementation CryptoHelper #pragma mark - #pragma mark Init Methods - (id)init { if(self = [super init]) { } return self; } #pragma mark - #pragma mark String Specific Methods /** * Encrypts a string for social blast service. * * @param plainString The string to encrypt; * * @return NSString The encrypted string. */ - (NSString *)encryptString: (NSString *) plainString{ // Convert string to data and encrypt NSData *data = [self encryptPBEWithMD5AndDESData:[plainString dataUsingEncoding:NSUTF8StringEncoding] password:@"1111"]; // Get encrypted string from data return [data base64EncodingWithLineLength:1024]; } /** * Descrypts a string from social blast service. * * @param plainString The string to decrypt; * * @return NSString The decrypted string. */ - (NSString *)decryptString: (NSString *) encryptedString{ // decrypt the data NSData * data = [self decryptPBEWithMD5AndDESData:[NSData dataWithBase64EncodedString:encryptedString] password:@"1111"]; // extract and return string return [NSString stringWithUTF8String:[data bytes]]; } #pragma mark - #pragma mark Crypto Methods - (NSData *)encryptPBEWithMD5AndDESData:(NSData *)inData password:(NSString *)password { return [self encodePBEWithMD5AndDESData:inData password:password direction:1]; } - (NSData *)decryptPBEWithMD5AndDESData:(NSData *)inData password:(NSString *)password { return [self encodePBEWithMD5AndDESData:inData password:password direction:0]; } - (NSData *)encodePBEWithMD5AndDESData:(NSData *)inData password:(NSString *)password direction:(int)direction { NSLog(@"helper data = %@", inData); static const char gSalt[] = { (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0xAA, (unsigned char)0x00 }; unsigned char *salt = (unsigned char *)gSalt; int saltLen = strlen(gSalt); int iterations = 15; EVP_CIPHER_CTX cipherCtx; unsigned char *mResults; // allocated storage of results int mResultsLen = 0; const char *cPassword = [password UTF8String]; unsigned char *mData = (unsigned char *)[inData bytes]; int mDataLen = [inData length]; SSLeay_add_all_algorithms(); X509_ALGOR *algorithm = PKCS5_pbe_set(NID_pbeWithMD5AndDES_CBC, iterations, salt, saltLen); memset(&cipherCtx, 0, sizeof(cipherCtx)); if (algorithm != NULL) { EVP_CIPHER_CTX_init(&(cipherCtx)); if (EVP_PBE_CipherInit(algorithm->algorithm, cPassword, strlen(cPassword), algorithm->parameter, &(cipherCtx), direction)) { EVP_CIPHER_CTX_set_padding(&cipherCtx, 1); int blockSize = EVP_CIPHER_CTX_block_size(&cipherCtx); int allocLen = mDataLen + blockSize + 1; // plus 1 for null terminator on decrypt mResults = (unsigned char *)OPENSSL_malloc(allocLen); unsigned char *in_bytes = mData; int inLen = mDataLen; unsigned char *out_bytes = mResults; int outLen = 0; int outLenPart1 = 0; if (EVP_CipherUpdate(&(cipherCtx), out_bytes, &outLenPart1, in_bytes, inLen)) { out_bytes += outLenPart1; int outLenPart2 = 0; if (EVP_CipherFinal(&(cipherCtx), out_bytes, &outLenPart2)) { outLen += outLenPart1 + outLenPart2; mResults[outLen] = 0; mResultsLen = outLen; } } else { unsigned long err = ERR_get_error(); ERR_load_crypto_strings(); ERR_load_ERR_strings(); char errbuff[256]; errbuff[0] = 0; ERR_error_string_n(err, errbuff, sizeof(errbuff)); NSLog(@"OpenSLL ERROR:\n\tlib:%s\n\tfunction:%s\n\treason:%s\n", ERR_lib_error_string(err), ERR_func_error_string(err), ERR_reason_error_string(err)); ERR_free_strings(); } NSData *encryptedData = [NSData dataWithBytes:mResults length:mResultsLen]; //(NSData *)encr_buf; //NSLog(@"encryption result: %@\n", [encryptedData base64EncodingWithLineLength:1024]); EVP_cleanup(); return encryptedData; } } EVP_cleanup(); return nil; } @end 

我想感谢wbyoung。 他的回答帮助我解决了如何从iOSencryption数据并从Java解密的奥秘。 以下是我对“守则”的补充。

诀窍是拿出用来encryption数据的盐,并将其join到结果中。 Java将能够解密它。

 @implementation NSData (PBEEncryption) - (NSData *)encryptPBEWithMD5AndDESUsingPassword:(NSData *)password { unsigned char gSalt[] = { (unsigned char)0x18, (unsigned char)0x79, (unsigned char)0x6D, (unsigned char)0x6D, (unsigned char)0x35, (unsigned char)0x3A, (unsigned char)0x6A, (unsigned char)0x60, (unsigned char)0x00 }; NSData *salt = nil; salt = [NSData dataWithBytes:gSalt length:strlen(gSalt)]; NSData* encrypted = [self encryptPBEWithMD5AndDESUsingPassword:password salt:salt iterations:1000]; NSMutableData* result = [NSMutableData dataWithData:salt]; [result appendData:encrypted]; return [NSData dataWithData:result]; } - (NSData *)encryptPBEWithMD5AndDESUsingPassword:(NSData *)password salt:(NSData *)salt iterations:(NSUInteger)iterations { unsigned char md5[CC_MD5_DIGEST_LENGTH] = {}; CC_MD5_CTX ctx; CC_MD5_Init(&ctx); CC_MD5_Update(&ctx, [password bytes], [password length]); CC_MD5_Update(&ctx, [salt bytes], [salt length]); CC_MD5_Final(md5, &ctx); for (NSUInteger i = 1; i < iterations; i++) { CC_MD5(md5, CC_MD5_DIGEST_LENGTH, md5); } // initialization vector is the second half of the MD5 from building the key unsigned char iv[kCCBlockSizeDES]; assert(kCCBlockSizeDES == CC_MD5_DIGEST_LENGTH / 2); memcpy(iv, md5 + kCCBlockSizeDES, sizeof(iv)); NSMutableData *output = [NSMutableData dataWithLength:([self length] + kCCBlockSize3DES)]; size_t outputLength = 0; CCCryptorStatus status = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionPKCS7Padding, md5, kCCBlockSizeDES, iv, [self bytes], [self length], [output mutableBytes], [output length], &outputLength); if (status == kCCSuccess) { [output setLength:outputLength]; } else { output = nil; } return output; } @end 

对应的encryptionstring的Swift代码:

 static func encryptForOverTheWire(string: String) -> String { let stringData = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) let passwordData = PASSWORD.dataUsingEncoding(NSUTF8StringEncoding) let encryptedData = stringData?.encryptPBEWithMD5AndDESUsingPassword(passwordData) let base64 = encryptedData?.base64EncodedDataWithOptions(NSDataBase64EncodingOptions()) guard base64 != nil else { return "" } let result = String(data: base64!, encoding: NSUTF8StringEncoding) return result ?? "" }