Diffie Hellman exchange for ECDH-ES not working properly, getting wrong secret key

HI all,

I am trying to implement diffie-hellman key exchnage, by generating a secret key with an EC public key receives from API and the private key already generated in the project. Also I need to perform the KDF operation by passing some parameters. Here what my project code look like

generating the secret key

func generateSharedSecret(issuerPublicKey: SecKey, devicePrivateKey: SecKey, parameters:[SecKeyKeyExchangeParameter: Any] = [:]) throws -> Data? {

    var error: Unmanaged<CFError>?

    guard let shared = SecKeyCopyKeyExchangeResult(devicePrivateKey, .ecdhKeyExchangeStandard, issuerPublicKey, parameters as CFDictionary, &error) else {

        let errStr = error?.takeRetainedValue().localizedDescription ?? "Derive Key Fail"

        print(errStr)

        throw error!.takeRetainedValue() as Error

    }
    return shared as Data
}

Setup parameters for performing secret key operation and KDF

var algId: Data, keyDataLen: Int
            algId = "".data(using: .utf8)!
            keyDataLen = 256
            let algorithmID = prefixedBigEndenLen(from: algId)
            let partyUInfo = prefixedBigEndenLen(from: apu)
            let partyVInfo = prefixedBigEndenLen(from: apv)
            let suppPubInfo = intToData(value: UInt32(keyDataLen).bigEndian)
            let suppPrivInfo = Data()

            let concatedData = algorithmID + partyUInfo + partyVInfo + suppPubInfo + suppPrivInfo

            let params = [SecKeyKeyExchangeParameter.requestedSize: 32, SecKeyKeyExchangeParameter.sharedInfo: concatedData] as [SecKeyKeyExchangeParameter : Any]

            // Function call:
            let sharedSecret = try generateSharedSecret(issuerPublicKey: pubKey, devicePrivateKey: eprivKey, parameters: params as [SecKeyKeyExchangeParameter : Any])

By using the resulting generated secret key I have performed the JWE encryption and got some encrypted string as output. The problem what I am facing it is not decrypting on the server side and server returns "Unable to parse error".

Can anyone let me know, is it the correct way to generate a secret key? What am I doing wrong here?

Thanks

Answered by DTS Engineer in 729319022

Debugging problems like this is hard. My suggestion is that you pull the problem apart and debug one step at a time. You wrote:

What I am understanding is the secret key generated in Server and iOS side is somehow different.

In that case I recommend that you test that theory specifically. You and your server team should share a pair of private keys (from which you can derive the public keys) and a shared info value, and then each use those to derive a session key. If you get different results, you know that’s the problem.

Having said that, I took a look at your code and it clearly has issues. You’re using the .ecdhKeyExchangeStandard algorithm but you’re also supplying SecKeyKeyExchangeParameter values. The doc comments for kSecKeyAlgorithmECDHKeyExchangeStandard are clear that:

This algorithm does not accept any parameters, length of output raw
shared secret is given by the length of the key.

I suspect your server team is using a different algorithm, one that includes a KDF, but it’s hard to say which one based on the info you’ve presented so far.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

By using the resulting generated secret key I have performed the JWE encryption and got some encrypted string as output. The problem what I am facing it is not decrypting on the server side and server returns "Unable to parse error".

Do you have any evidence that the encryption is the problem here? That Unable to parse error is pretty generic and you have a lot of other stuff that’s unrelated to your encryption that could be causing this problem, like all that big endian byte formatting.

My general advice in situations like this is to step through the code on the server side to see exactly what’s triggering the error.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi,

Thanks for responding my post. The server team shared their log for understanding the exact reason why the encrypted data is not decrypting there side.

Authentication tag check failed. Message=U_eZPiksc-vC65hHU-NRNw calculated=U7GzQubkdmKyz9dHELXrdQ key = 2F3095EB64D0209F0693D5E280249222EDFE27620F4FBF9E5EE673920106546B

What I am understading is the secret key generated in Server and iOS side is somehow different. How did we generate secret key and perform KDF operation on iOS? Is there any wrong with my code logic?

Thank you

Accepted Answer

Debugging problems like this is hard. My suggestion is that you pull the problem apart and debug one step at a time. You wrote:

What I am understanding is the secret key generated in Server and iOS side is somehow different.

In that case I recommend that you test that theory specifically. You and your server team should share a pair of private keys (from which you can derive the public keys) and a shared info value, and then each use those to derive a session key. If you get different results, you know that’s the problem.

Having said that, I took a look at your code and it clearly has issues. You’re using the .ecdhKeyExchangeStandard algorithm but you’re also supplying SecKeyKeyExchangeParameter values. The doc comments for kSecKeyAlgorithmECDHKeyExchangeStandard are clear that:

This algorithm does not accept any parameters, length of output raw
shared secret is given by the length of the key.

I suspect your server team is using a different algorithm, one that includes a KDF, but it’s hard to say which one based on the info you’ve presented so far.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi,

Thanks for your support. I appreciate that.

As you suggestted we have share the private key and it seems different on the servewr side and iOS side. When I am checking the ANDROID code they are generating a secret key and then perform a KDF operation by using ConcatKDF with the help of a third party.

Now I have modified the code as you've suggested

            let params = [SecKeyKeyExchangeParameter.requestedSize: 32] as [SecKeyKeyExchangeParameter : Any]

            // Function call:
            guard let sharedSecret = try generateSharedSecret(issuerPublicKey: pubKey, devicePrivateKey: privateKey, parameters: params as [SecKeyKeyExchangeParameter : Any]) else {
                return nil

            }

I need to perform a KDF operation by including these six elements (listed below) into the generated secret key. How did I do the same KDF operation here in iOS? Please help me to know where would I need to modify the current code?

`            var algId: Data, keyDataLen: Int
            algId = "".data(using: .utf8)!
            keyDataLen = 256
            let algorithmID = prefixedBigEndenLen(from: algId)
            let partyUInfo = prefixedBigEndenLen(from: apu)
            let partyVInfo = prefixedBigEndenLen(from: apv)
            let suppPubInfo = intToData(value: UInt32(keyDataLen).bigEndian)`

Thank you again for keep responding to my message. Thanks

How did I do the same KDF operation here in iOS?

Until today I wasn’t familiar with ConcatKDF. AFAIK there’s no iOS API that implements that. I recommend that you ask your server folks to support a more common KDF, and specifically X9.63. If they won’t do that, you’ll have to implement ContactKDF yourself. It requires A) a bunch of byte manipulation, B) a hash function, and C) various fixed inputs. You’ll have to ask your server folks about B and C. I also recommend that you track down some test vectors, ’cause getting this working otherwise is going to be a challenge.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Diffie Hellman exchange for ECDH-ES not working properly, getting wrong secret key
 
 
Q