2

我正在尝试找到一个库来使用 Scrypt 算法实现密码散列(带盐)。我的问题类似于 stackoverflow 中已经提出的问题(Swift 应用程序中的哈希密码

我分别在 swift 和 Objective c 中找到了以下两个库,但是从这些库生成的哈希字符串与在服务器上生成的不匹配。

  1. 斯威夫特钠(https://github.com/jedisct1/swift-sodium
  2. 氯化钠(https://github.com/gabriel/NAChloride

有人可以帮忙寻找可用于 Swift 3.0 iOS 应用程序的库,用于使用盐进行密码散列。

谢谢你。

问候,

纳格拉吉瓦德吉尔

4

2 回答 2

1

我找到了自己问题的答案,想到了分享,因为它对其他人有用。

服务器团队使用 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=

于 2017-09-01T05:49:22.137 回答
0

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>
于 2017-08-31T04:09:45.093 回答