Post marked as solved
Post marked as solved with 11 replies, 1,557 views
Hello, guys!
I'm having trouble to validate JWT with EC algorithm in iOS.
I have generated JWT and public key from jwt.io using the ES384 algorithm and I have the following validator:
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
)
}
}
Initially I had some troubles to get the SecKey, but the ASN1Decoder solved that issue. Now I'm struggling to get it validated. I'm always getting false. I don't know what SecKeyAlgorithm to use.
These are the errors I'm getting:
EC signature verification failed (ccerr -7)
or
algorithm not supported by the key <SecKeyRef curve type: kSecECCurveSecp384r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 384 bits, y: B2F393DFD51470F2513920F516A60BB9E774AD78A8A088A2D43DE3696EA9986549161A1DCC2DF5732E6C7CE628B6BCC5, x: 0B5B964978F6733083C0C4CB595E41166C3174CE8F5FDA7E3E4F587FDAC87F7EF89B95CFD54F2AEFD74184B488B9AA23, addr: 0x102e26140>
Any help will be highly appreciated!
Edit
If I use RS256 algorithm from jwt.io, without ASN1Decoder and
let algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA256
there's no issue at all.