简体   繁体   English

读取 Swift 中 Java 服务器发送的公钥

[英]Reading Public Key Sent by Java Server in Swift

I'm trying to read a public key (x509 format encoded) from a Java Server to finalize my Elliptic Curve Diffie Hellman Exchange.我正在尝试从 Java 服务器读取公钥(x509 格式编码)以完成我的椭圆曲线 Diffie Hellman Exchange。 I can send the Public Key to the server with no problem, but now I want to read the public Key that the server has sent to the iOS Client.我可以毫无问题地将公钥发送到服务器,但现在我想读取服务器发送给 iOS 客户端的公钥。

byte[] serverPubKeyEnc = serverKpair.getPublic().getEncoded(); (This is on the server)

This is what I return to the iOS side.这就是我返回到 iOS 方面的内容。 For me to deal with it, I need to read it from the input stream and then turn it into a usable public key.为了处理它,我需要从输入 stream 中读取它,然后将其转换为可用的公钥。 This is what I have right now on the iOS side of things to read the key:这就是我现在在 iOS 方面的阅读关键:

 var error: Unmanaged<CFError>? = nil
    
    let mutableData = CFDataCreateMutable(kCFAllocatorDefault, CFIndex(0))
        if mutableData != nil
        {
            let headerSize = 26
         
            //For importing Java key data
            CFDataAppendBytes(mutableData, CFDataGetBytePtr(data as CFData), CFDataGetLength(data as CFData))
            CFDataDeleteBytes(mutableData, CFRangeMake(CFIndex(0), headerSize))

            //Use the mutableData here (SecKeyCreateWithData)
            let publicKey = SecKeyCreateWithData(
                mutableData!,
                [
                    kSecAttrKeyType: kSecAttrKeyTypeEC,
                    kSecAttrKeyClass: kSecAttrKeyClassPublic,
                ] as NSDictionary,
                &error)
            
            let fullKey = SecKeyCopyExternalRepresentation(publicKey!, &error)
            
            return fullKey!
        }

Here I can read the "publicKey", I know it has some value inside it.在这里我可以读到“publicKey”,我知道它里面有一些价值。 How can I turn this into a usable key for me to generate the shared secret?我怎样才能把它变成一个可用的密钥来生成共享密钥?

TLDR: I want to read a public key that comes from the Java server (ECDH) to generate a symmetric key for encryption in the iOS client. TLDR:我想读取来自 Java 服务器 (ECDH) 的公钥,以在 iOS 客户端中生成用于加密的对称密钥。

The complete flow would look like:完整的流程如下所示:

  • receiving 91 bytes with the public key from server side从服务器端使用公钥接收 91 个字节
  • create a SecKey with SecKeyCreateWithData使用 SecKeyCreateWithData 创建一个 SecKey
  • create a key pair on iOS with SecKeyCreateRandomKey使用 SecKeyCreateRandomKey 在 iOS 上创建密钥对
  • send own public key to server side将自己的公钥发送到服务器端
  • server side can compute the shared secret with that information服务器端可以使用该信息计算共享密钥
  • client computes a shared secret with SecKeyCopyKeyExchangeResult客户端使用 SecKeyCopyKeyExchangeResult 计算共享密钥
  • if everything is correct, it should give the same shared secret on iOS and Java side如果一切正确,它应该在 iOS 和 Java 端提供相同的共享密钥

Therefore, to get a complete test case, one can write a Java program that generates a key pair.因此,为了得到一个完整的测试用例,可以编写一个生成密钥对的 Java 程序。 For simplicity, one can copy/paste the public key between the Java and iOS app for a test instead of using a network connection.为简单起见,可以复制/粘贴 Java 和 iOS 应用程序之间的公钥进行测试,而不是使用网络连接。 The Java program writes the public key to the console. Java 程序将公钥写入控制台。 This key is copied into the Swift source code.将此密钥复制到 Swift 源代码中。 The Swift program is compiled and generates a key pair as well. Swift 程序已编译并生成密钥对。 The public key is copied / pasted to the Java program, which reads it on the console.公钥被复制/粘贴到 Java 程序,该程序在控制台上读取它。 Both programs then output the calculated shared secret, which should be the same for obvious reasons, since it is used for further symmetric encryption.两个程序然后 output 计算的共享密钥,由于显而易见的原因应该是相同的,因为它用于进一步的对称加密。

This fine answer https://stackoverflow.com/a/26502285/2331445 provides utility methods for converting a hex String to Data and back.这个很好的答案https://stackoverflow.com/a/26502285/2331445提供了将十六进制字符串转换为数据并返回的实用方法。

iOS Swift Code iOS Swift 代码

The following code assumes that a secp256r1 curve is used with a key size of 256 bit.以下代码假定使用 secp256r1 曲线,密钥大小为 256 位。

The described flow could be implemented as follows:所描述的流程可以实现如下:

    let otherKey = "3059301306072a8648ce3d020106082a8648ce3d03010703420004df96b3c0c651707c93418781b91782319f6e798550d954c46ac7318c7eac130f96380991a93049059e03e4190dd147b64d6ebc57320938f026844bda3de22352".hexadecimal!
    
    guard let otherPublicKey = otherPublicKey(data: otherKey) else { return }
    guard let ownPrivateKey = createOwnKey() else { return }
    guard let ownPublicKey = SecKeyCopyPublicKey(ownPrivateKey) else { return }
    
    send(ownPublicKey: ownPublicKey)
    
    if let sharedSecret = computeSharedSecret(ownPrivateKey: ownPrivateKey, otherPublicKey: otherPublicKey) {
        print("shared secret: \(sharedSecret.hexadecimal)")
    } else {
        print("shared secret computation failed")
    }
    

The used functions:用到的功能:

private func otherPublicKey(data: Data) -> SecKey? {
    var error: Unmanaged<CFError>? = nil
    
    let cfData = data.dropFirst(26) as CFData
    
    let attributes =  [
        kSecAttrKeyType: kSecAttrKeyTypeEC,
        kSecAttrKeyClass: kSecAttrKeyClassPublic,
    ] as CFDictionary
    
    if let publicKey = SecKeyCreateWithData(cfData, attributes, &error) {
        return publicKey
    }
    print("other EC public: \(String(describing: error))")
    return nil
}

private func createOwnKey() -> SecKey? {
    var error: Unmanaged<CFError>? = nil
    let keyPairAttr: [String : Any] = [kSecAttrKeySizeInBits as String: 256,
                                       kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
                                       kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]
    ]
    guard let key = SecKeyCreateRandomKey(keyPairAttr as CFDictionary, &error) else {
        print("key creation: \(String(describing: error))")
        return nil
    }
    return key
}

This function send only outputs the key in hex on the debug console.此 function send仅在调试控制台上以十六进制输出密钥。 For the test it can be transferred to the Java program via copy/paste.对于测试,可以通过复制/粘贴将其转移到 Java 程序中。 In a real program it would be transferred to the server via a network connection.在实际程序中,它将通过网络连接传输到服务器。

private func send(ownPublicKey: SecKey) {
    guard let data = SecKeyCopyExternalRepresentation(ownPublicKey, nil) as Data? else {
        print("SecKeyCopyExternalRepresentation failed")
        return
    }
    let secp256r1Header = "3059301306072a8648ce3d020106082a8648ce3d030107034200"
    let pkWithHeader = secp256r1Header + data.hexadecimal
    print("ownPublicKeyHexWithHeader \(pkWithHeader.count / 2) bytes: " + pkWithHeader)
}

With the own private key and the public key of the server, the shared secret can be computed.使用自己的私钥和服务器的公钥,可以计算共享秘密。

private func computeSharedSecret(ownPrivateKey: SecKey, otherPublicKey: SecKey) -> Data? {
    let algorithm:SecKeyAlgorithm = SecKeyAlgorithm.ecdhKeyExchangeStandard
    let params = [SecKeyKeyExchangeParameter.requestedSize.rawValue: 32, SecKeyKeyExchangeParameter.sharedInfo.rawValue: Data()] as [String: Any]
    
    var error: Unmanaged<CFError>? = nil
    if let sharedSecret: Data = SecKeyCopyKeyExchangeResult(ownPrivateKey, algorithm, otherPublicKey, params as CFDictionary, &error) as Data? {
        return sharedSecret
    } else {
        print("key exchange: \(String(describing: error))")
    }
    return nil
}

Test测试

In the upper area you can see the Xcode console and in the lower area the output of the Java program.在上方区域您可以看到 Xcode 控制台,在下方区域可以看到 Java 程序的 output。 The common secret is the same.共同的秘密是相同的。 So the test was successful.所以测试成功了。

测试 ECDH Java/Swift

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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