【问题标题】:Scrypt hash algorithm for password hashing in Swift 3.0 (iOS)Swift 3.0 (iOS) 中用于密码散列的 Scrypt 散列算法
【发布时间】:2017-08-31 03:21:44
【问题描述】:

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

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

  1. Swift-Sodium (https://github.com/jedisct1/swift-sodium)
  2. 氯化钠 (https://github.com/gabriel/NAChloride)

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

谢谢。

问候,

纳格拉吉瓦德吉尔

【问题讨论】:

    标签: ios swift hash salt scrypt


    【解决方案1】:

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

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

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

    1) Scrypt 版本 ($s0$)

    2) 参数 (这是使用以下公式计算的:

    字符串参数 = Long.toString(log2(N)

    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 -> 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、4、0x6c、07 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

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

    【讨论】:

      【解决方案2】:

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

      例子:

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

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

      本示例代码提供了多种哈希算法,包括 SHA1、SHA256、SHA512。

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

      此示例需要 Common Crypto
      项目必须有桥接头:
      #import &lt;CommonCrypto/CommonCrypto.h&gt;
      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>
      

      【讨论】:

      • 谢谢。我已经使用 CommonCrypto 库测试了 PBDKF2 解决方案,它工作正常,但我的项目要求我使用 Scrypt 哈希算法,因为它比 PBKDF2 更安全。请建议一个链接或库来为 Swift 3.0 项目实现 Scrypt 哈希算法。
      • 如果您已创建威胁模型(以书面形式)并确定 PBDKF2 和 Scrypt 之间的差异是最高漏洞,那么 Scrypt 是正确的选择。
      猜你喜欢
      • 2012-06-12
      • 2011-10-05
      • 2016-11-03
      • 1970-01-01
      • 2010-10-16
      • 2013-01-09
      • 2016-10-21
      • 1970-01-01
      • 2019-02-15
      相关资源
      最近更新 更多