swift iOS中使用ECDSASHA384算法通过公钥验证JWT

bvpmtnay  于 2023-03-28  发布在  Swift
关注(0)|答案(1)|浏览(133)

我在iOS中使用EC算法验证JWT时遇到问题。我使用ES 384算法从www.example.com生成了JWT和公钥jwt.io,我有以下验证器:

let jwtToken = "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.VUPWQZuClnkFbaEKCsPy7CZVMh5wxbCSpaAWFLpnTe9J0--PzHNeTFNXCrVHysAa3eFbuzD8_bLSsgTKC8SzHxRVSj5eN86vBPo_1fNfE7SHTYhWowjY4E_wuiC13yoj"
let publicKey = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC1uWSXj2czCDwMTLWV5BFmwxdM6PX9p+Pk9Yf9rIf374m5XP1U8q79dBhLSIuaojsvOT39UUcPJROSD1FqYLued0rXiooIii1D3jaW6pmGVJFhodzC31cy5sfOYotrzF"
let isValid = JWTValidator.validateSignature(forToken: jwtToken, withPublicKey: publicKey)
print(isValid) // always false
import ASN1Decoder

class JWTValidator {
    static func validateSignature(forToken token: String, withPublicKey publicKeyText: String) -> Bool {
        let parts = token.components(separatedBy: ".")

        let header = parts[0]
        let payload = parts[1]
        let signature = parts[2]
        
        guard let dataPublicKey = Data(base64Encoded: publicKeyText),
              let dataSigned = (header + "." + payload).data(using: .ascii),
              let dataSignature = Data(base64Encoded: base64StringWithPadding(base64str: signature)) else {
            print("Failed to get signature!")
            return false
        }
        var secKeyCreateError : Unmanaged<CFError>?
        guard let publicKey: SecKey = DerDecoder().decodePublicKey(dataPublicKey,&secKeyCreateError) else {
            print("Failed to create SecKey : %@", secKeyCreateError!.takeRetainedValue().localizedDescription)
            return false
        }

        var validateError : Unmanaged<CFError>?
        let algorithm: SecKeyAlgorithm = .ecdsaSignatureMessageX962SHA384
        let result = SecKeyVerifySignature(peerPublicKey,
                                           algorithm,
                                           dataSigned as NSData,
                                           dataSignature as NSData,
                                           &validateError)
        if let validateError = validateError {
            print(validateError)
        }
        return result
    }
    
    static func base64StringWithPadding(base64str: String) -> String {
        var newStr = base64str.replacingOccurrences(of: "-", with: "+")
                    .replacingOccurrences(of: "_", with: "/")
        let count = newStr.count % 4
        if count > 0 {
          let amount = 4 - count
          for _ in 0..<amount {
              newStr += "="
          }
        }
        return newStr
     }
}

class DerDecoder {
    func decodePublicKey(_ data: Data,  _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> SecKey? {
        guard
            let asn1 = try? ASN1DERDecoder.decode(data: data),
            let keyData = asn1.first?.sub(1)?.value as? Data
        else {
            return nil
        }
        return SecKeyCreateWithData(
            keyData as CFData,
            [
                kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
                kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            ] as CFDictionary,
            error
        )
    }
}

最初我在获取SecKey时遇到了一些麻烦,但ASN 1Decoder解决了这个问题。现在我正在努力验证它。我总是得到false。我不知道该使用什么SecKeyAlgorithm
这些是我得到的错误:
EC签名验证失败(ccerr -7)

密钥〈SecKeyRef曲线类型不支持算法:kSecECCurveSecp 384 r1,算法id:3、按键类型:ECPublicKey,版本:4、区块大小:384位,y:B2F393DFD51470F2513920F516A60BB9E774AD78A8A088A2D43DE3696EA9986549161A1DCC2DF5732E6C7CE628B6BCC5,x:0 B5 B 964978 F6733083 C 0 C4 CB 595 E41166 C3174 CE 8 F5 FDA 7 E3 E4 F587 FDAC 87 F7 EF 89 B 95 CFD 54 F2 AEFD 74184 B488 B 9AA 23,地址:0x102e26140〉
如果我使用www.example.com上的RS 256算法jwt.io,而不使用ASN 1Decoder

let algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA256

完全没有问题。
任何帮助都将不胜感激。

whlutmcx

whlutmcx1#

对于那些正在努力解决Apple关于该主题的缺失文档的人来说-这是Apple开发者论坛中建议的工作示例:

import Foundation
import CryptoKit

class JWTValidator {
    static func validateSignature() {
        let jwtToken = "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.VUPWQZuClnkFbaEKCsPy7CZVMh5wxbCSpaAWFLpnTe9J0--PzHNeTFNXCrVHysAa3eFbuzD8_bLSsgTKC8SzHxRVSj5eN86vBPo_1fNfE7SHTYhWowjY4E_wuiC13yoj"

        let publicKeyBase64 = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC1uWSXj2czCDwMTLWV5BFmwxdM6PX9p+Pk9Yf9rIf374m5XP1U8q79dBhLSIuaojsvOT39UUcPJROSD1FqYLued0rXiooIii1D3jaW6pmGVJFhodzC31cy5sfOYotrzF"

        let parts = jwtToken.components(separatedBy: ".")

        let header = parts[0]
        let payload = parts[1]
        let signature = parts[2]

        let dataPublicKey = Data(base64Encoded: publicKeyBase64)!
        let dataSigned = (header + "." + payload).data(using: .ascii)!
        let dataSignature = Data(base64Encoded: base64StringWithPadding(base64str: signature))!
        let dataSignatureX962 = try! P384.Signing.ECDSASignature(rawRepresentation: dataSignature).derRepresentation

        if #available(iOS 14.0, *) {
            let ck = try! P384.Signing.PublicKey(derRepresentation: dataPublicKey)
            
            let x963 = ck.x963Representation
            let publicKey = SecKeyCreateWithData(x963 as NSData, [
                kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
                kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            ] as NSDictionary, nil)!
            print(publicKey)
            
            //we have the SecKey representation of the public key.
            //validate the JWT with the public key.
            
            var validateError: Unmanaged<CFError>?
            let algorithm: SecKeyAlgorithm = .ecdsaSignatureMessageX962SHA384
            
            let result = SecKeyVerifySignature(publicKey,
                                               algorithm,
                                               dataSigned as NSData,
                                               dataSignatureX962 as NSData,
                                               &validateError)
            
            if let validateError = validateError {
                print(validateError)
            }
            
            print("JWT is valid: \(result)")
        }
    }
    
    static func base64StringWithPadding(base64str: String) -&gt; String {
        var newStr = base64str.replacingOccurrences(of: "-", with: "+")
                    .replacingOccurrences(of: "_", with: "&#x2F;")
        let count = newStr.count % 4
        if count &gt; 0 {
          let amount = 4 - count
          for _ in 0..&lt;amount {
              newStr += "="
          }
        }
        return newStr
     }
}

JWTValidator.validateSignature()

所以我的代码中的问题是:

  • 要使用的正确算法是.ecdsaSignatureMessageX962SHA384
  • 原始签名必须转换为X9.62格式(示例中的dataSignatureX 962属性)

相关问题