I've generated 2 sets of private/public keys and exported to pem, now I'm trying to import them into both Swift & a JS script but they seem to be generating different shared keys so swift can't decrypt a message from JS and vice versa. Given the fact that I'm able to import the keys then export them and the output matches the original input, I'm sure they key pairs are fine so my issue must lie in deriving the shared key.
In swift I'm using CryptoKit and JS Webcrypto.
JS:
async function deriveKey(publicKey, privateKey) {
return await crypto.subtle.deriveKey(
{ name: "ECDH", public: publicKey },
privateKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
}
let sharedKey1 = await deriveKey(publicKey2, privateKey);
let exportedKey = await crypto.subtle.exportKey('raw', sharedKey1);
let keyString = Buffer.from(exportedKey).toString('base64');
console.log(keyString);
Which outputs: 1vF4AK9IqDDHNZ86zxt5zavx3h+V7AFCfpBU5Yv8Zro=
Swift:
func deriveSymmetricKey(privateKey: P256.KeyAgreement.PrivateKey, publicKey: P256.KeyAgreement.PublicKey) throws -> SymmetricKey {
let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: publicKey)
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: Data(),
sharedInfo: Data(),
outputByteCount: 32
)
return symmetricKey
}
let sharedKey1 = try deriveSymmetricKey(privateKey: privateKey, publicKey: publicKey2)
let keyData = sharedKey1.withUnsafeBytes {
Data(Array($0))
}
let keyString = keyData.base64EncodedString()
print(keyString)
Which outputs: SeQZEg38dcfRl8+5LIwiiJXABJnOMuv3srlrIDZ0wQc=
The Swift code derives a key from the shared secret using HKDF. This step is missing in the WebCrypto code and can be implemented there as follows:
This results in the same key that the Swift code provides.
Note that it is not necessary to export the shared secret (raw or as Base64 encoded data), but that it can be processed end-to-end as a
CryptoKey
. For this, however, yourderiveKey()
method must be adapted: