我在iOS中遇到PBEWithMD5AndDES加密问题。我使用这个https://gist.github.com/788840/24bc73ecd0ac3134cbd242892c74a06ac561d37b加密和解密字符串。
问题是我获得了不同的加密值,具体取决于我的方法所在的类。例如,我将所有加密方法移动到辅助类中并运行它。我注意到我得到了一个不同的加密值。
我现在在不同的类中有两个相同版本的相同方法,我并排运行它们。他们获得不同的加密值,而且无法解密其他人的加密值。我有点难过。
这是执行加密/解密的帮助程序类。
@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) {
}
}
答案 0 :(得分:6)
接受的答案似乎是使用未包含在iOS SDK中的OpenSSL。这是一个加密和解密解决方案,它使用包含的CommonCrypto库(在libSystem中)。我有点像ObjC n00b,所以请把这段代码带上一粒盐。顺便说一句,这段代码将成功解密使用java的PBEWithMD5AndDES密码加密的数据。希望这会为其他人节省一两天。
#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;
}
}
答案 1 :(得分:3)
问题在于您没有为加密指定IV,这样会自动为您生成随机数,这也是您每次获得不同结果的原因。
尝试使用PKCS5_pbe2_set_iv
代替PKCS5_pbe2_set
提供明确的IV值,您可以随机选择,就像您的盐值一样。
答案 2 :(得分:3)
使用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]];
答案 3 :(得分:2)
不确定这里有什么协议来接受答案/提出答案。如果我做错了,我道歉。答案结果证明盐中没有最后一个字节。我实际上不需要使用3DES加密的IV。我赞成了另一个答案,因为它有助于更多地了解加密。
这是最终目标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
答案 4 :(得分:0)
我要感谢wbyoung。他的回答帮助我解决了如何从iOS加密数据并将其从Java解密的谜团。以下是我对他的代码所做的补充。
诀窍是使用用于加密数据的salt,并将其添加到结果中。然后,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
加密字符串的相应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 ?? ""
}
答案 5 :(得分:0)
通过这种方式,我可以使其与Swift一起使用。我面临的唯一问题是我从目标C模块中获取了一个Data对象,但是当我尝试转换为String时,它的值为nil。这是代码。请让我知道我在这里做错了什么
static func encrypt(inString: String) -> String? {
let passwordStr = "blablalba"
let iterations:Int32 = 19
let salt = [0x44, 0x44, 0x22, 0x22, 0x56, 0x35, 0xE3, 0x03] as [UInt8]
let operation: CCOperation = UInt32(kCCEncrypt)
let saltData = NSData(bytes: salt, length: salt.count)
let out = CryptoHelper.cryptPBE(withMD5AndDES: operation, using: inString.data(using: .utf8), withPassword: passwordStr, andSalt: saltData as Data, andIterating: iterations) as Data
let outString = String.init(data: out, encoding: .utf8)
return outString
}