代码之家  ›  专栏  ›  技术社区  ›  Andrew_STOP_RU_WAR_IN_UA

如何从密码生成SymmetricKey

  •  0
  • Andrew_STOP_RU_WAR_IN_UA  · 技术社区  · 8 月前

    我有以下代码:

    public extension SymmetricKey {
        init?(base64Encoded base64EncodedKeyString: String) {
            guard let keyData = Data(base64Encoded: base64EncodedKeyString) else { return nil }
            self.init(data: keyData)
        }
        
        var base64: String {
            self.withUnsafeBytes { body in
                Data(body).base64EncodedString()
            }
        }
        
        init?(pass: String) {
            guard let passHash = passToHash(pass: pass) else { return nil }
            
            self.init(base64Encoded: passHash)
        }
    }
    
    fileprivate func passToHash(pass: String) -> String? {
        guard let passData = pass.data(using: .utf8) else { return nil }
        let hashed = SHA256.hash(data: passData)
        return hashed.compactMap { String(format: "%02x", $0) }.joined()
    }
    

    问题是我是否会尝试使用 SymmetricKey 我将看到错误:

    操作无法完成。(CryptoKit.CryptoKitError错误1。)

    此错误表示密钥大小不正确。

    如果我试着比较一下 对称密钥 s以默认方式生成,从我的自定义init中我会看到区别:

    5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5 //from password
    cfyk1v2dRIlVKHlpy8qBH71v4BXGSUHPUS3wLn1gzj0= //.bits256
    

    enter image description here

    我做错了什么? 为什么哈希看起来不同?

    1 回复  |  直到 8 月前
        1
  •  1
  •   Rob Napier    8 月前

    首先,这是一个非常不安全的密钥推导函数。任何严肃的加密方案都不应该使用这个。要从密码创建密钥,必须使用PBKDF(基于密码的密钥派生函数),如PBKDF2。

    出于兼容性的目的,您可能需要实现这个破碎的方案,所以我将在这里深入探讨。

    密钥基本上是数据。您已经创建了一个十六进制编码的字符串,然后对其进行了Base64编码。这使得结果数据的长度是它应该的两倍。你的意思是:

    public extension SymmetricKey {
        var base64: String {
            self.withUnsafeBytes { body in
                Data(body).base64EncodedString()
            }
        }
    
        // Never do this. This is incredibly insecure
        init(pass: String) {
            // This is a completely broken KDF.
            let hash = SHA256.hash(data: Data(pass.utf8)).withUnsafeBytes { ptr in
                Data(ptr)
            }
            self.init(data: hash)
        }
    }
    
    let key1 = SymmetricKey(size: .bits256).base64
    // ZHpD7LwSwhOT0msWf0XOJItqUezMSLwdxcLloSMSMHs
    let key2 = SymmetricKey(pass: "12345").base64
    // WZRHGrsBESr8wYFZ9sx0tPURuZgG2lmzyvWpwXPKz8U
    
    // key2 is very easy to brute-force compared to proper AES-256
    

    不幸的是,苹果的CryptoKit不包括任何适当的PBKDF。它是一个非常有限的框架,只为一些非常特定的用例提供原语。提供所需工具的一些流行框架是 CryptoSwift IDZSwiftCommonCrypto 。更通用的方法是使用苹果的CommonCryptor,但Swift并不是特别有趣。

    然而,这里有一个例子。

    public extension SymmetricKey {
        var base64: String {
            self.withUnsafeBytes { body in
                Data(body).base64EncodedString()
            }
        }
    
        init(password: String, salt: Data) {
            self.init(data: Self.makeKey(forPassword: password, withSalt: salt))
        }
    
        private static func makeKey(forPassword password: String, withSalt salt: Data) -> Data {
    
            let passwordArray = password.utf8.map(Int8.init)
            let saltArray = Array(salt)
            let keySize = 32
    
            var derivedKey = Array<UInt8>(repeating: 0, count: keySize)
    
            // All the crazy casting because CommonCryptor hates Swift
            let algorithm    = CCPBKDFAlgorithm(kCCPBKDF2)
            let prf          = CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1)
            let pbkdf2Rounds = UInt32(10000)
    
            let result = CCCryptorStatus(
                CCKeyDerivationPBKDF(
                    algorithm,
                    passwordArray, passwordArray.count,
                    saltArray,     saltArray.count,
                    prf,           pbkdf2Rounds,
                    &derivedKey,   keySize)
            )
            guard result == CCCryptorStatus(kCCSuccess) else {
                fatalError("SECURITY FAILURE: Could not derive secure password (\(result))")
            }
            return Data(derivedKey)
        }
    }
    

    称之为需要传递盐。如何选择盐在一定程度上取决于你的使用情况。一种常见的情况是,每次加密都使用随机盐(比如16个字节),然后将盐与加密数据一起传递,以便接收器可以解密。这是一种制作这种随机数据的方法:

    func makeSalt(length: Int) -> Data {
        var data = Data(count: length)
        data.withUnsafeMutableBytes {
            SecRandomCopyBytes(kSecRandomDefault, length, $0.baseAddress!)
        }
        return data
    }
    

    您还可以在上述框架中获得执行这些操作的函数。在Swift中做密码学几乎没有什么乐趣。

    推荐文章