Swift 3.0(iOS)中用于密码散列的Scrypt哈希算法
我正在尝试使用Scrypt算法找到一个库来实现密码散列(使用salt)。 我的问题类似于stackoverflow中已经提出的问题( Swift应用程序中的哈希密码 )
我分别在swift和objective c中找到了两个库,但是从这些库生成的哈希字符串与在服务器上生成的哈希字符串不匹配。
- Swift-Sodium( https://github.com/jedisct1/swift-sodium )
- NAChloride( https://github.com/gabriel/NAChloride )
有人可以帮助找到可用于Swift 3.0 iOS应用程序的库,用于使用salt进行密码散列。
谢谢。
问候,
Nagraj Wadgire
Common Crypto包含PBKDF2,这是NIST推荐的密码散列函数。
例:
基于密码的密钥派生2(Swift 3)
基于密码的密钥派生既可用于从密码文本中导出加密密钥,也可用于保存密码以进行身份validation。
可以使用几种哈希算法,包括SHA1,SHA256,SHA512,这些算法由此示例代码提供。
rounds参数用于使计算变慢,以便攻击者必须在每次尝试上花费大量时间。 典型的延迟值在100ms到500ms之间,如果有不可接受的性能,可以使用更短的值。
此示例需要Common Crypto
有必要为项目建立一个桥接头:
#import
将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>
我找到了自己的问题的答案,想到分享,因为它对其他人有用。
服务器团队使用Scrypt库( https://github.com/wg/scrypt )为给定的密码和salt生成哈希字符串。
在分析了服务器端库之后,我们发现生成的哈希字符串包含以下组件。
1)Scrypt版本($ s0 $)
2)params(使用以下公式计算:
String params = Long.toString(log2(N)<< 16L | r << 8 | p,16))
3)salt以base64字符串格式
4)生成base64字符串格式的派生密钥
最终哈希字符串的格式是$ s0 $ params $ salt $ key
(有关更多信息,请参阅此问题什么是scrypt输出的最大长度? )
如问题中所述,我在客户端使用NAChloride库生成哈希字符串。
该类包含以下生成哈希字符串的方法:
open class func scrypt(_密码:Data!,salt:Data!,n N:UInt64,r:UInt32,p:UInt32,length:Int)throws – > Data
在我们的示例中,我们传递了以下值:
n = 16,
r = 16,
p = 16,
长度(字节)= 32,
salt =数据(字节:[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])
此方法将仅生成“数据”格式的派生密钥,因此我认为与服务器端生成的密钥相比,它是不同的。
在生成派生密钥以匹配服务器上生成的哈希字符串(服务器端哈希字符串格式)的格式之后,我必须编写逻辑。
下面是用Swift 3.0编写的代码,使用内部使用Scrypt哈希算法的NAChloride库为给定密码生成哈希字符串:
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 =