我正在尝试找到一个库来使用 Scrypt 算法实现密码散列(带盐)。我的问题类似于 stackoverflow 中已经提出的问题(Swift 应用程序中的哈希密码)
我分别在 swift 和 Objective c 中找到了以下两个库,但是从这些库生成的哈希字符串与在服务器上生成的不匹配。
有人可以帮忙寻找可用于 Swift 3.0 iOS 应用程序的库,用于使用盐进行密码散列。
谢谢你。
问候,
纳格拉吉瓦德吉尔
我正在尝试找到一个库来使用 Scrypt 算法实现密码散列(带盐)。我的问题类似于 stackoverflow 中已经提出的问题(Swift 应用程序中的哈希密码)
我分别在 swift 和 Objective c 中找到了以下两个库,但是从这些库生成的哈希字符串与在服务器上生成的不匹配。
有人可以帮忙寻找可用于 Swift 3.0 iOS 应用程序的库,用于使用盐进行密码散列。
谢谢你。
问候,
纳格拉吉瓦德吉尔
我找到了自己问题的答案,想到了分享,因为它对其他人有用。
服务器团队使用 Scrypt 库 ( https://github.com/wg/scrypt ) 为给定的密码和盐生成哈希字符串。
在分析了服务器端库后,我们了解到生成的哈希字符串包含以下组件。
1) Scrypt 版本 ($s0$)
2)参数(这是使用以下公式计算的:
字符串参数 = Long.toString(log2(N) << 16L | r << 8 | p, 16))
3)base64字符串格式的盐
4) 生成base64字符串格式的派生键
最终哈希字符串的格式为$s0$params$salt$key
(有关更多信息,请参阅此问题scrypt 输出的最大长度是多少?)
如问题中所述,我在客户端使用NChloride库来生成哈希字符串。
此类包含以下用于生成哈希字符串的方法:
open class func scrypt(_ password: Data!, salt: Data!, n N: UInt64, r: UInt32, p: UInt32, length: Int) throws -> Data
在我们的示例中,我们传递了以下值:
n = 16,
r = 16,
p = 16,
长度(字节)= 32,
盐=数据(字节:[0x73、0x61、0x6c、0x74、0x44、0x61、0x74、0x61、0x73、0x61、0x6c、0x74、0x44、0x61、0x74、0x61、0x73、0x61、0x46c、0x714、0x , 0x74, 0x61,0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61])
此方法将仅生成“数据”格式的派生密钥,因此我认为它与在服务器端生成的密钥相比有所不同。
在生成派生密钥以匹配在服务器上生成的哈希字符串(服务器端哈希字符串格式)的格式后,我必须编写一个逻辑。
下面是用 Swift 3.0 编写的代码,用于使用内部使用Scrypt哈希算法的NChloride库为给定密码生成哈希字符串:
func passwordHashingUsingScrypt(password: String) -> String{
let N = 16
let r = 16
let p = 16
let term1 = Int(log2(Double(N))) << 16
let term2 = r << 8
let paramsDecimal = term1 | term2 | p
let params = String(format:"%2X", paramsDecimal)
print(params)
let message = password.data(using:.utf8)!
let salt = Data(bytes:[0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61,0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61,0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61,0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61])
let saltBase64String = salt.base64EncodedString()
print(saltBase64String)
let hashData = try! NAScrypt.scrypt(message, salt: salt, n: 16, r: 16, p: 16, length: 32)
let hashBase64String = hashData.base64EncodedString()
print(hashBase64String)
let result = saltBase64String+"$"+hashBase64String
print(result)
var hashString = String()
hashString.append("$s0$")
hashString.append(params)
hashString.append("$")
hashString.append(saltBase64String)
hashString.append("$")
hashString.append(hashBase64String)
print(hashString)
return hashString
}
您还可以使用以下方法生成随机盐:
func randomBytes(numberOfBytes:Int) -> [UInt8] {
var randomBytes = [UInt8](repeating: 0, count: numberOfBytes) // array to hold randoms bytes
let result = SecRandomCopyBytes(kSecRandomDefault, numberOfBytes, &randomBytes)
print(result)
return randomBytes
}
结果:
密码:admin1234<
哈希字符串: $s0$41010$c2FsdERhdGFzYWx0RGF0YXNhbHREYXRhc2FsdERhdGE=$GrMF1P3VH8YrgUEaOJDVSc4as/XTSWhCbbp4DLie00I=
Common Crypto 包含 PBKDF2,它是 NIST 推荐的密码散列函数。
例子:
基于密码的密钥派生 2 (Swift 3)
基于密码的密钥派生既可用于从密码文本派生加密密钥,也可用于保存密码以进行身份验证。
此示例代码提供了多种哈希算法,包括 SHA1、SHA256、SHA512。
rounds 参数用于使计算变慢,以便攻击者每次尝试都必须花费大量时间。典型的延迟值在 100 毫秒到 500 毫秒之间,如果性能不可接受,可以使用更短的值。
此示例需要 Common Crypto
项目必须有一个桥接头:
#import <CommonCrypto/CommonCrypto.h>
将其添加Security.framework
到项目中。
参数:
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
}
示例用法:
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)")
示例输出:
derivedKey (SHA1): <6b9d4fa3 0385d128 f6d196ee 3f1d6dbf>