I noticed that you are using kCCHmacAlgSHA1 instead of kCCPRFHmacAlgSHA1 , this is probably an error.
This works for me:
NSData *keyData = [@"password" dataUsingEncoding:NSUTF8StringEncoding]; NSData *salt = [@"salt" dataUsingEncoding:NSUTF8StringEncoding]; uint rounds = 2048; uint keySize = kCCKeySizeAES128; NSMutableData *derivedKey = [NSMutableData dataWithLength:keySize]; CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm keyData.bytes, // password keyData.length, // passwordLength salt.bytes, // salt salt.length, // saltLen kCCPRFHmacAlgSHA1, // PRF rounds, // rounds derivedKey.mutableBytes, // derivedKey derivedKey.length); // derivedKeyLen NSLog(@"derivedKey: %@", derivedKey);
Output:
derivedKey: <2c13cb7a d3468748 5c3f3d4f 18ebddbd>
Swift 3
let password = "TestPassword" let salt = Data("salt" .utf8); let keySize = kCCKeySizeAES128; let rounds = 2048 var derivedKeyData = Data(repeating:0, count:keySize) let derivationStatus = derivedKeyData.withUnsafeMutableBytes {derivedKeyBytes in salt.withUnsafeBytes { saltBytes in CCKeyDerivationPBKDF( CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, saltBytes, salt.count, UInt32(kCCPRFHmacAlgSHA1), UInt32(rounds), derivedKeyBytes, derivedKeyData.count) } } print("derivedKeyData: \(derivedKeyData.map { String(format: "%02hhx", $0) }.joined())")
Output:
derivedKeyData: 75d1f85fd64d170b3ffe66c7f1d1519a
A more general solution from the section of hardened documentation:
Password output based on password 2 (Swift 3 +)
Password-based key derivation can be used both to obtain the encryption key from the password text, and to save the password for authentication purposes.
There are several hashing algorithms that can be used, including SHA1, SHA256, SHA512, which are provided by this sample code.
The rounds parameter is used to make the calculation slow so that the attacker has to spend considerable time on each attempt. Typical delay values ββfall in the range from 100 ms to 500 ms, shorter values ββcan be used if there is unacceptable performance.
This example requires Common Crypto. You must have a title header for the project:
#import <CommonCrypto/CommonCrypto.h>
Add Security.framework to the project.
Options:
password password String salt salt Data keyByteCount number of key bytes to generate rounds Iteration rounds returns Derived key func pbkdf2SHA1(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA1), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds) } func pbkdf2SHA256(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds) } func pbkdf2SHA512(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds) } func pbkdf2(hash :CCPBKDFAlgorithm, password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { let passwordData = password.data(using:String.Encoding.utf8)! var derivedKeyData = Data(repeating:0, count:keyByteCount) let derivationStatus = derivedKeyData.withUnsafeMutableBytes {derivedKeyBytes in salt.withUnsafeBytes { saltBytes in CCKeyDerivationPBKDF( CCPBKDFAlgorithm(kCCPBKDF2), password, passwordData.count, saltBytes, salt.count, hash, UInt32(rounds), derivedKeyBytes, derivedKeyData.count) } } if (derivationStatus != 0) { print("Error: \(derivationStatus)") return nil; } return derivedKeyData }
Usage example:
let password = "password" //let salt = "saltData".data(using: String.Encoding.utf8)! let salt = Data(bytes: [0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61]) let keyByteCount = 16 let rounds = 100000 let derivedKey = pbkdf2SHA1(password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds) print("derivedKey (SHA1): \(derivedKey! as NSData)")
Result:
derivedKey (SHA1): <6b9d4fa3 0385d128 f6d196ee 3f1d6dbf>
Password-based key output calibration (Swift 3 +)
Determine the number of PRF rounds to use for a specific delay on the current platform.
Several parameters are defaulted for representative values, which should not significantly affect the number of rounds.
password Sample password. salt Sample salt. msec Targeted duration we want to achieve for a key derivation. returns The number of iterations to use for the desired processing time. func pbkdf2SHA1Calibrate(password: String, salt: Data, msec: Int) -> UInt32 { let actualRoundCount: UInt32 = CCCalibratePBKDF( CCPBKDFAlgorithm(kCCPBKDF2), password.utf8.count, salt.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), kCCKeySizeAES256, UInt32(msec)); return actualRoundCount }
Usage example:
let saltData = Data(bytes: [0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61]) let passwordString = "password" let delayMsec = 100 let rounds = pbkdf2SHA1Calibrate(password:passwordString, salt:saltData, msec:delayMsec) print("For \(delayMsec) msec delay, rounds: \(rounds)")
Result:
For 100 msec delay, rounds: 93457