老项目里关于AES埋的坑

起因


// YYCategories / NSData+YYAdd / AES256解密方法(虽然方法名写的是AES256,实际上用的是AES128)
- (NSData *)aes256DecryptWithkey:(NSData *)key iv:(NSData *)iv {
    if (key.length != 16 && key.length != 24 && key.length != 32) {
        return nil;
    }
    if (iv.length != 16 && iv.length != 0) {
        return nil;
    }
    
    NSData *result = nil;
    size_t bufferSize = self.length + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    if (!buffer) return nil;
    size_t encryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          key.bytes,
                                          key.length,
                                          iv.bytes,
                                          self.bytes,
                                          self.length,
                                          buffer,
                                          bufferSize,
                                          &encryptedSize);
    if (cryptStatus == kCCSuccess) {
        result = [[NSData alloc]initWithBytes:buffer length:encryptedSize];
        free(buffer);
        return result;
    } else {
        free(buffer);
        return nil;
    }
}

一句话真因

可以用的AES256解密方法

// aes256解密
- (NSString *)aes256Decrypt:(NSString *)ciphertext withPassword:(NSString *)password {
    if (ciphertext.length == 0 || password.length == 0) {
        return nil;
    }

    NSData *cipherData = [[NSData alloc] initWithBase64EncodedString:ciphertext options:0];
    if (!cipherData) {
        return nil;
    }

    const char saltHeader[] = "Salted__"; // OpenSSL salted prefix

    NSData *decrypted = nil;

    if (cipherData.length > 16 && memcmp(cipherData.bytes, saltHeader, 8) == 0) {
        // OpenSSL salted format: "Salted__" + 8 bytes salt + encrypted
        NSData *salt = [cipherData subdataWithRange:NSMakeRange(8, 8)];
        NSData *encData = [cipherData subdataWithRange:NSMakeRange(16, cipherData.length - 16)];

        // Derive key and IV using OpenSSL EVP_BytesToKey (MD5)
        NSMutableData *derived = [NSMutableData data];
        NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
        NSMutableData *previous = nil;

        while (derived.length < (kCCKeySizeAES256 + kCCBlockSizeAES128)) {
            // MD5(previous + password + salt) using one-shot CC_MD5; wrap with pragma to silence deprecation diagnostics
            NSMutableData *mdInput = [NSMutableData data];
            if (previous) {
                [mdInput appendData:previous];
            }
            [mdInput appendData:passwordData];
            [mdInput appendData:salt];

            unsigned char md[CC_MD5_DIGEST_LENGTH];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
            CC_MD5(mdInput.bytes, (CC_LONG)mdInput.length, md);
#pragma clang diagnostic pop

            previous = [NSMutableData dataWithBytes:md length:CC_MD5_DIGEST_LENGTH];
            [derived appendBytes:md length:CC_MD5_DIGEST_LENGTH];
        }

        NSData *key = [derived subdataWithRange:NSMakeRange(0, kCCKeySizeAES256)];
        NSData *iv = [derived subdataWithRange:NSMakeRange(kCCKeySizeAES256, kCCBlockSizeAES128)];

        size_t outLength = encData.length + kCCBlockSizeAES128;
        void *outBuf = malloc(outLength);
        size_t numBytesDecrypted = 0;

        CCCryptorStatus status = CCCrypt(kCCDecrypt,
                                         kCCAlgorithmAES,
                                         kCCOptionPKCS7Padding,
                                         key.bytes,
                                         key.length,
                                         iv.bytes,
                                         encData.bytes,
                                         encData.length,
                                         outBuf,
                                         outLength,
                                         &numBytesDecrypted);

        if (status == kCCSuccess) {
            decrypted = [NSData dataWithBytesNoCopy:outBuf length:numBytesDecrypted freeWhenDone:YES];
        } else {
            free(outBuf);
            return nil;
        }
    } else {
        // Fallback: assume raw AES256-CBC with key = SHA256(password), iv = zeros
        NSData *encData = cipherData;
        NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
        unsigned char keybuf[CC_SHA256_DIGEST_LENGTH];
        CC_SHA256(passwordData.bytes, (CC_LONG)passwordData.length, keybuf);

        unsigned char ivZero[kCCBlockSizeAES128];
        memset(ivZero, 0, sizeof(ivZero));

        size_t outLength = encData.length + kCCBlockSizeAES128;
        void *outBuf = malloc(outLength);
        size_t numBytesDecrypted = 0;

        CCCryptorStatus status = CCCrypt(kCCDecrypt,
                                         kCCAlgorithmAES,
                                         kCCOptionPKCS7Padding,
                                         keybuf,
                                         kCCKeySizeAES256,
                                         ivZero,
                                         encData.bytes,
                                         encData.length,
                                         outBuf,
                                         outLength,
                                         &numBytesDecrypted);

        if (status == kCCSuccess) {
            decrypted = [NSData dataWithBytesNoCopy:outBuf length:numBytesDecrypted freeWhenDone:YES];
        } else {
            free(outBuf);
            return nil;
        }
    }

    if (!decrypted) return nil;

    NSString *plaintext = [[NSString alloc] initWithData:decrypted encoding:NSUTF8StringEncoding];
    return plaintext;
}


Written on September 20, 2025