Open smamczak opened 6 years ago
Hi! Thanks for letting me know about this use case!
It was slightly tricky. 2 things where off.
I managed to produce a valid JWToken
eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJ0ZXN0dXNlcm5hbWUiLCJzdWIiOiJ0ZXN0Y2xpZW50aWQiLCJpYXQiOjE1MDE1MDk3ODIsImV4cCI6MTUwMTUwOTg0Mn0.9bUeggjl9sOdfxqOOcMv7uE6MUBrhEMWEFDLI1xiD4HwO8oSK_TuveST4imEug13h7hthgy1uwYPa0Fb7M4N3w
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXum2ZYo0Qp2foJRIKPP2eGNT82y6
GgZgRGKWB8DArDhKQAhjp/RQFCoP8Olq4QtL5l4jcdKZhvOTAd47r7tAvQ==
-----END PUBLIC KEY-----
Here's the code
import EllipticCurveKeyPair
extension Data {
func base64EncodedStringURISafe() -> String {
return self.base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}
func DEREC256SignatureDecode(_ signature: Data) -> Data {
// example https://lapo.it/asn1js/#3046022100F309E36DFA5FE0BFBF5C3855E06E9C3C7D04DE347E2B345C3392DDB98E13BE6302210080372B3BBAE5E370B976092B8AA64F4EF1025FFE893D0046FA085F256AE04761
var decoded = signature
let maxChunkSize = 32
decoded.removeFirst() // removing sequence header
decoded.removeFirst() // removing sequence size
decoded.removeFirst() // removing 'r' element header
let rLength = Int(decoded.removeFirst()) // removing 'r' element length
let r = decoded.prefix(rLength).suffix(maxChunkSize) // read out 'r' bytes and discard any padding
decoded.removeFirst(Int(rLength)) // removing 'r' bytes
decoded.removeFirst() // 's' element header
let sLength = Int(decoded.removeFirst()) // 's' element length
let s = decoded.prefix(sLength).suffix(maxChunkSize) // read out 's' bytes and discard any padding
return Data(r) + Data(s)
}
let keysManager: EllipticCurveKeyPair.Manager = {
let publicAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAlwaysThisDeviceOnly, flags: [])
let privateAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, flags: [.privateKeyUsage, .userPresence])
let config = EllipticCurveKeyPair.Config(
publicLabel: "test.sign.public",
privateLabel: "test.sign.private",
operationPrompt: "Json web token",
publicKeyAccessControl: publicAccessControl,
privateKeyAccessControl: privateAccessControl,
token: .secureEnclave)
return EllipticCurveKeyPair.Manager(config: config)
}()
func testJWT() {
do {
try? keysManager.deleteKeyPair()
let headerAndPayload = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJ0ZXN0dXNlcm5hbWUiLCJzdWIiOiJ0ZXN0Y2xpZW50aWQiLCJpYXQiOjE1MDE1MDk3ODIsImV4cCI6MTUwMTUwOTg0Mn0"
let derSignature = try keysManager.sign(headerAndPayload.data(using: .utf8)!, hash: .sha256)
let decodedSignature = DEREC256SignatureDecode(derSignature)
let token = headerAndPayload + "." + decodedSignature.base64EncodedStringURISafe()
print("headerAndPayloadAsString: \(headerAndPayload)")
print("signature: \(derSignature.base64EncodedStringURISafe())")
print("decoded signature: \(decodedSignature.base64EncodedStringURISafe())")
print("token: \(token)")
print("public key: \n\(try keysManager.publicKey().data().PEM)")
try keysManager.verify(signature: derSignature,
originalDigest: headerAndPayload.data(using: .utf8)!,
hash: .sha256)
print("valid!")
} catch {
print("error: \(error)")
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
testJWT()
}
There's some mutual interests here https://github.com/yourkarma/JWT/issues/116
Hi, everything works! I'm really grateful for the speed of response and example!
Great! I'm keeping this open until I have this included somehow
That would be nice too. The sign method could have a param like so
enum SignatureFormat {
case der
case raw
}
public func sign(_ digest: Data, hash: Hash, format: SignatureFormat = .der, context: LAContext? = nil) throws -> Data {
}
but this is just a suggestion
👍👍 Yep, either that or return an object with two variables.
Hi, I wanto to use your library for my project which uses JSON Web Token JWT = base64(header) + "." + base64(payload) + "." + base64(signature)
In the documentation of my project I have an example
And when I paste that token here https://jwt.io/ with the public key - it validates. However when I use your library to sign the header and payload I get info about invalid signature (on JWT page)
The verification passes your method
Unbased content of header and payload are
Am I doing something wrong with signing?