微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决在 Swift 中读取 Java 服务器发送的公钥

我正在尝试从 Java 服务器读取公钥(x509 格式编码)以完成我的 Elliptic Curve Diffie Hellman Exchange。我可以毫无问题地将公钥发送到服务器,但现在我想读取服务器发送给 iOS 客户端的公钥。

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

这就是我回到iOS端。为了处理它,我需要从输入流中读取它,然后将其转换为可用的公钥。这就是我现在在 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!
        }

在这里我可以读取“publicKey”,我知道它里面有一些价值。我怎样才能把它变成一个可用的密钥来生成共享秘密?

TLDR:我想读取来自 Java 服务器 (ECDH) 的公钥以生成用于在 iOS 客户端中加密的对称密钥。

解决方法

完整的流程如下:

  • 从服务器端接收带有公钥的 91 个字节
  • 使用 SecKeyCreateWithData 创建一个 SecKey
  • 使用 SecKeyCreateRandomKey 在 iOS 上创建密钥对
  • 将自己的公钥发送到服务器端
  • 服务器端可以使用该信息计算共享密钥
  • 客户端使用 SecKeyCopyKeyExchangeResult 计算共享密钥
  • 如果一切正确,它应该在 iOS 和 Java 端提供相同的共享密钥

因此,要获得完整的测试用例,可以编写一个生成密钥对的 Java 程序。为简单起见,可以在 Java 和 iOS 应用程序之间复制/粘贴公钥以进行测试,而不是使用网络连接。 Java 程序将公钥写入控制台。此密钥已复制到 Swift 源代码中。 Swift 程序被编译并生成一个密钥对。公钥被复制/粘贴到 Java 程序,后者在控制台上读取它。然后两个程序都输出计算出的共享密钥,出于显而易见的原因,这应该是相同的,因为它用于进一步的对称加密。

这个很好的答案 https://stackoverflow.com/a/26502285/2331445 提供了将十六进制字符串转换为数据并返回的实用方法。

iOS Swift 代码

以下代码假设使用 secp256r1 曲线,密钥大小为 256 位。

所描述的流程可以实现如下:

    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")
    }
    

用到的函数:

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
}

此函数 send 仅在调试控制台上以十六进制输出密钥。对于测试,它可以通过复制/粘贴传输到 Java 程序。在实际程序中,它将通过网络连接传输到服务器。

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)
}

有了自己的私钥和服务器的公钥,就可以计算出共享秘密。

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
}

测试

在上部区域您可以看到 Xcode 控制台,在下部区域中可以看到 Java 程序的输出。共同的秘密是一样的。所以测试成功了。

Test ECDH Java/Swift

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。