简体   繁体   中英

swift Generate shared key using ECDH

I am trying to encrypt a message to the server. To do this, I generate a public and private key. I send my public key to the server and receive its public key from it.

func generatePair() {
    if let crypto = GMEllipticCurveCrypto.generateKeyPair(for: GMEllipticCurveSecp256r1) {
        crypto.compressedPublicKey = false
        curveCrypto = crypto
        if let publicKey = crypto.publicKey {
            let pub = Data(publicKey[1...publicKey.count-1])
            self.publicKey = pub.base64EncodedString()
            self.privateKey = crypto.privateKeyBase64
        }
    }
}

Upon receipt, I try to generate a shared key to encrypt the data.

func generateSecret(withKey key: String) {
    guard let crypto = curveCrypto else {
        return
    }
    print("generateSecret \(key)")
    sharedKey = crypto.sharedSecret(forPublicKeyBase64: key)
}

But I get the error in line (sharedKey = crypto.sharedSecret(forPublicKeyBase64: key))

*** Terminating app due to uncaught exception 'Invalid Key', reason: 'Public key {length = 64, bytes = 0xace3000d e1483ed9 82d88432 9397c716 ... d64e0fe1 47920d9f } is invalid'

My public key and server key are the same length.

What is I doing wrong?

I believe you are using GMEllipticCurveCrypto by ricmoo.

This framework use compressed keys internally. Which is why it will always fail as the length of the input key(65) doesn't match the expected length of compressed key(33).

Note that the function sharedSecret(forPublicKeyBase64:) will call sharedSecret(forPublicKey:) internally. So the final solution is to compress the key again before passing it to sharedSecret(forPublicKey:) . You can choose to expose compressPublicKey function in header file or use update the function in .m file with the solution below.

Original:

// Prepare the public key
- (NSData*)sharedSecretForPublicKey: (NSData*)otherPublicKey {
    [...]
    uint8_t l_other_public[_bytes + 1];
    if ([otherPublicKey length] != _bytes + 1) {
        [NSException raise:@"Invalid Key" format:@"Public key %@ is invalid", otherPublicKey];
    }
    [...]
}

Fix:

- (NSData*)sharedSecretForPublicKey: (NSData*)otherPublicKey {
    NSData* compressedKey = [self compressPublicKey:otherPublicKey];
    [...]
    // Prepare the public key
    uint8_t l_other_public[_bytes + 1];
    if ([compressedKey length] != _bytes + 1) {
        [NSException raise:@"Invalid Key" format:@"Public key %@ is invalid", compressedKey];
    }
    [compressedKey getBytes:&l_other_public length:[compressedKey length]];
    [...]
}

Finally I have found the solution.

After receiving public key from server I should do this:

private func getPublicKey(publicKey: String) -> String? {
    if let publicKeyData = Data(base64Encoded: publicKey) {
        var newKeyData = publicKeyData
        newKeyData.insert(0x04, at: 0)
        if let compressKeyData = curveCrypto?.compressPublicKey(newKeyData) {
            return compressKeyData.base64EncodedString()
        } else {
            print("PUB KEY ERROR: compressKeyData")
            return nil
        }
    } else {
        print("PUB KEY ERROR: b64")
        return nil
    }
}

And this public key should be used for generation shared key.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM