Swift 3.0(iOS)中用于密码散列的Scrypt哈希算法

时间:2017-08-31 03:21:45

标签: ios swift hash salt scrypt

我正在尝试使用Scrypt算法找到一个库来实现密码散列(使用salt)。 我的问题类似于stackoverflow(Hash password in Swift application

中已经提出的问题

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

  1. Swift-Sodium(https://github.com/jedisct1/swift-sodium
  2. NAChloride(https://github.com/gabriel/NAChloride
  3. 有人可以帮助找到可以用于Swift 3.0 iOS应用程序的库,用于使用salt进行密码散列。

    谢谢。

    此致

    Nagraj Wadgire

2 个答案:

答案 0 :(得分:1)

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

服务器团队正在使用Scrypt库(https://github.com/wg/scrypt)为给定的密码和salt生成哈希字符串。

在分析了服务器端库之后,我们发现生成的哈希字符串包含以下组件。

1)Scrypt版本($ s0 $)

#p> 2)params (这是使用以下公式计算的:

String params = Long.toString(log2(N)<< 16L | r<< 8 | p,16))

3)base64字符串格式的盐

4)以base64字符串格式生成的派生密钥

最终哈希字符串的格式为 $ s0 $ params $ salt $ key

(有关详细信息,请参阅此问题What's the is maximum length of scrypt output?

如问题中所述,我在客户端使用了 NAChloride 库来生成哈希字符串。

此类包含以下生成哈希字符串的方法:

open class func scrypt(_ password:Data!,salt:Data!,n N:UInt64,r:UInt32,p:UInt32,length:Int)throws - >数据

在我们的示例中,我们传递了以下值:

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编写的代码,使用 NAChloride 库生成给定密码的哈希字符串,该库内部使用 Scrypt 哈希算法:

    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&LT;

哈希字符串: $ s0 $ 41010 $ c2FsdERhdGFzYWx0RGF0YXNhbHREYXRhc2FsdERhdGE = $ GrMF1P3VH8YrgUEaOJDVSc4as / XTSWhCbbp4DLie00I =

答案 1 :(得分:0)

Common Crypto包含PBKDF2,它是NIST推荐的密码散列函数。

示例:

基于密码的密钥派生2(Swift 3)

基于密码的密钥派生既可用于从密码文本中导出加密密钥,也可用于保存密码以进行身份​​验证。

可以使用几种哈希算法,包括SHA1,SHA256,SHA512,这些算法由此示例代码提供。

rounds参数用于使计算变慢,以便攻击者必须在每次尝试上花费大量时间。典型的延迟值在100ms到500ms之间,如果有不可接受的性能,可以使用更短的值。

此示例需要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>