-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Secure Enclave and signing #186
Comments
@hakonk Yes, somewhere in future these API methods could replace existing API methods. For now we could add one more node in inheritance tree ( Look at implementation of RS algorithms ). If you have ready-to-use solution - feel free to PR :) |
Thanks for replying so quickly! If I find a solution, I'll see if I can make a PR :) |
@hakonk |
@hakonk |
@lolgear Great! I'll have a look :) |
After looking into the code base, I'm having some difficulties understanding how this can be used with the existing implementation. While signing the secret is provided as a string with
I can't seem to figure out how I should use the existing API to sign using a Thanks in advance, I appreciate you taking the time to pursue the issue. |
@hakonk First of all, you could set keys as Look at convenient setters in
Now you want to provide a key for that. This library assume that your keys are stored in So, you only need to add method that set SecKeyRef for JWTCryptoKey items. But here is another trouble which could be avoided. So, at the end you have something like: JWTCryptoKeyPrivate *key = [[JWTCryptoKeyPrivate alloc] initWithRawSecKeyRef:keyRef];
holder.signKey(key); UPD: |
@hakonk |
Hi, sorry for the late reply! I will have a look at the implementation :) |
@hakonk how did you get on with this? |
@bencallis Since I ended up approaching the problem in a different way, I no longer was dependent on this library, and thus I didn't look into what was needed to make it work with the signatures created in the Secure Enclave. Are you looking into the issue yourself? |
Hi may I know any updates on this? |
@AyeChanPyaeSone
@protocol JWTCryptoKey__Raw__Generator__Protocol
- (instancetype)initWithSecKeyRef:(SecKeyRef)key;
@end |
JWTCryptoKeyPrivate *key = [[JWTCryptoKeyPrivate alloc] initWithSecKeyRef:privateKey];
is this correct approach? I am using the algorith "ES256" please let me know is there anything wrong. However when i pass to server the signature seems invalid. |
thanks you very much for your help 👍 |
I have to add secretData because it forced me to add secretData. |
@AyeChanPyaeSone Thanks! |
Hi @lolgear, thanks for developing and maintaining this library for such a long time. I'm currently experimenting with secure enclave keys and ran into this issue:
This results in that error:
Digging into In If I manually override the values the encode succeeds without error. Decoding however (both with JWT and jwt.io) fails. Do you have any hints? Btw. I'm on Thanks! |
@b00tsy Algorithm in JWT is a name of preferred algorithm for encryption. On one side we have public property of desired encryption ( JWT part ), on other side - private property of a key entity. Would you like to map private property of key to a public property of JWT? Public JWT property -> Security Algorithm ( nice good ) You do everything correct, but what you really want is to specify algorithm type ( by public JWT name ) to allow and Next, what you can do in this version ( I don't test it ) is to set algorithmName directly var signDataHolder = JWTAlgorithmRSFamilyDataHolder.createWithAlgorithm256().algorithmName(@"ES256"); You can dump key to its string representation, as I remember, by calling @interface JWTCryptoSecurity (ExternalRepresentation)
+ (NSData *)externalRepresentationForKey:(SecKeyRef)key error:(NSError *__autoreleasing*)error;
@end |
Thanks for the quick response. This works without error (both signing and verifying): let paylod = ["deviceId": "123123123"]
let privateJWTKey = JWTCryptoKeyPrivate(secKeyRef: privateKeyRef)
let publicJWTKey = JWTCryptoKeyPublic(secKeyRef: publicKeyRef)
var signDataHolder = JWTAlgorithmRSFamilyDataHolder.createWithAlgorithm256()
signDataHolder = signDataHolder?.algorithmName("ES256")
signDataHolder = signDataHolder?.signKey(privateJWTKey)
signDataHolder = signDataHolder?.verifyKey(publicJWTKey)
signDataHolder = signDataHolder?.secretData(Data()) // setting .secretData(Data()) is currently mandatory because of a bug
let signBuilder = JWTEncodingBuilder.encodePayload(paylod)?.addHolder(signDataHolder)
let signResult = signBuilder?.encode
let signResultSuccess = signResult?.successResult
let encoded = signResultSuccess?.encoded
let signResultError = signResult?.errorResult
let signError = signResultError?.error
let verifyBuilder = JWTDecodingBuilder.decodeMessage(encoded)?.addHolder(signDataHolder)
let verifyResult = verifyBuilder?.decode
let verifyResultSuccess = verifyResult?.successResult
let headers = verifyResultSuccess?.headers
let payload = verifyResultSuccess?.payload
let claimsSet = verifyResultSuccess?.claimsSet
let verifyResultError = verifyResult?.errorResult
let verifyError = verifyResultError?.error However, verifying externally fails (jwt.io, or nodejs jsonwebtoken or jose). jsonwebtoken is very specific about the error: Any hints? PS sorry about trashing your issues with help requests. We could make a howto afterwards for how to use the secure enclave with your library (all other JWT libraries have no support for secure enclave at all)... |
@b00tsy Could you attach example project with keys that you are using? Maybe some kind of aligning or unnecessary symbols at the end of signature could cause this issue. |
@b00tsy BTW, I think that .secretData(Data()) // setting .secretData(Data()) is currently mandatory because of a bug is unnecessary in new release. |
The private key itself is not exportable (within secure enclave chip on the processor). Call it from objective-c via Create a separate swift file with this: import EllipticCurveKeyPair
struct KeyPairDemo {
static let manager: EllipticCurveKeyPair.Manager = {
let publicAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAlwaysThisDeviceOnly, flags: [])
let privateAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, flags: [.privateKeyUsage])
let publicLabel = "key.public.test"
let privateLabel = "key.private.test"
let config = EllipticCurveKeyPair.Config(
publicLabel: publicLabel,
privateLabel: privateLabel,
operationPrompt: "PROMPT!?!",
publicKeyAccessControl: publicAccessControl,
privateKeyAccessControl: privateAccessControl,
publicKeyAccessGroup: nil,
privateKeyAccessGroup: nil,
fallbackToKeychainIfSecureEnclaveIsNotAvailable: true)
return EllipticCurveKeyPair.Manager(config: config)
}()
}
@objcMembers class TestKeyManager: NSObject {
class func publicKeyRef() -> SecKey? {
do {
let publicKey = try KeyPairDemo.manager.publicKey()
let publicKeyRef = publicKey.underlying
return publicKeyRef
} catch {
print("\(error)")
}
return nil
}
class func privateKeyRef() -> SecKey? {
do {
let privateKey = try KeyPairDemo.manager.privateKey()
let privateKeyRef = privateKey.underlying
return privateKeyRef
} catch {
print("\(error)")
}
return nil
}
class func testECKeyJWT() {
do {
let privateKeyRef = TestKeyManager.privateKeyRef()
let publicKeyRef = TestKeyManager.publicKeyRef()
let paylod = ["deviceId": "123123123"]
let privateJWTKey = JWTCryptoKeyPrivate(secKeyRef: privateKeyRef)
let publicJWTKey = JWTCryptoKeyPublic(secKeyRef: publicKeyRef)
var signDataHolder = JWTAlgorithmRSFamilyDataHolder.createWithAlgorithm256()
signDataHolder = signDataHolder?.algorithmName("ES256")
signDataHolder = signDataHolder?.signKey(privateJWTKey)
signDataHolder = signDataHolder?.verifyKey(publicJWTKey)
signDataHolder = signDataHolder?.secretData(Data()) // setting .secretData(Data()) is currently mandatory because of a bug
let signBuilder = JWTEncodingBuilder.encodePayload(paylod)?.addHolder(signDataHolder)
let signResult = signBuilder?.encode
let signResultSuccess = signResult?.successResult
let encoded = signResultSuccess?.encoded
let signResultError = signResult?.errorResult
let signError = signResultError?.error
print("\(encoded)")
let verifyBuilder = JWTDecodingBuilder.decodeMessage(encoded)?.addHolder(signDataHolder)
let verifyResult = verifyBuilder?.decode
let verifyResultSuccess = verifyResult?.successResult
let headers = verifyResultSuccess?.headers
let payload = verifyResultSuccess?.payload
let claimsSet = verifyResultSuccess?.claimsSet
let verifyResultError = verifyResult?.errorResult
let verifyError = verifyResultError?.error
print("\(payload)")
} catch {
print("\(error)")
}
}
} |
A very simple approach to circumvent all the issues (incompatiblity with other third party tools regarding jwt, encryption, signing and validation) created with the key and the key format of keys from the secure encalve could be to save a normal RSA key in the keychain and encrypt it on top of that with a key that's in the secure enclave... That would have the best ratio of compatibility and security for me... |
Hi! Thanks for making this awesome lib. I have a question regarding Secure Enclave and signing on the iOS platform (i.e., with devices affording this capability).
From reading the documentation, it seems the private key used for signing has to be passed via a
String
orData
instance. However, if the private key is generated and stored in the secure enclave it is my understanding it cannot be retrieved and thus not be used with the current implementation ofyourcarma/JWT
. From what I understand, signing will have to be done viaSecKeyCreateSignature
where the private key will be passed in as aSecKey
(see code example below). Do you have any plans for supporting this in the future?Regards,
Håkon
The text was updated successfully, but these errors were encountered: