PBEWithMD5AndDES Шифрование в iOS - PullRequest
2 голосов
/ 23 августа 2011

У меня проблема с шифрованием PBEWithMD5AndDES в iOS. У меня есть строки шифрования и дешифрования с помощью этого, 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) {
    }
}

Ответы [ 6 ]

6 голосов
/ 16 сентября 2011

В принятом ответе используется OpenSSL, который не включен в iOS SDK.Вот решение для шифрования и дешифрования, которое использует включенную библиотеку 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;
    }
}
3 голосов
/ 23 мая 2013

Используя ответ johwayner, я немного исправил это для моих целей расшифровки Java PBEWithMD5AndDES из jasypt . Вот что я закончил:

@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 голосов
/ 23 августа 2011

Проблема в том, что вы не указываете IV для своего шифрования, таким образом, для вас будет автоматически генерироваться случайное число, и это также причина, почему вы каждый раз получаете другой результат.

Попробуйте использовать PKCS5_pbe2_set_iv вместо PKCS5_pbe2_set, предоставив явное значение IV, которое вы можете выбрать случайным образом, очень похожее на значение соли.

2 голосов
/ 29 августа 2011

Не уверен, что протокол здесь для принятия ответов / голосования.Я прошу прощения, если я делаю это неправильно.Ответом оказалось отсутствие последнего байта в соли.Я на самом деле не нуждался в IV с шифрованием 3DES.Я проголосовал против другого ответа, потому что это помогло понять больше о шифровании.

Вот заключительная цель класса 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
0 голосов
/ 02 июля 2018

Кстати, я могу заставить его работать со Swift. Единственная проблема, с которой я сталкиваюсь, это то, что я получаю объект Data из целевого модуля C, но когда я пытаюсь преобразовать его в String, он дает мне ноль. вот код Пожалуйста, дайте мне знать, что я делаю здесь не так

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
}
0 голосов
/ 11 февраля 2016

Я хочу поблагодарить wbyoung.Его ответ помог мне разгадать тайну о том, как зашифровать данные с iOS и расшифровать их с Java.Ниже приведены дополнения, которые я сделал к его Кодексу.

Хитрость заключается в том, чтобы взять соль, использованную для шифрования данных, и добавить ее к результату.Затем 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 ?? ""
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...