在 iOS/Swift 中创建并导出为 base64 的 RSA 公钥在 Java 中无法识别 [英] RSA public key created in iOS/Swift and exported as base64 not recognized in Java

查看:213
本文介绍了在 iOS/Swift 中创建并导出为 base64 的 RSA 公钥在 Java 中无法识别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL;DR:无法识别在 iOS 中生成并存储在钥匙串中、以 base64 格式导出并发送到 java 后端的 RSA 公钥.

我正在 iOS 应用中实现聊天加密功能,我使用对称 + 非对称密钥来处理它.

在不涉及太多细节的情况下,在后端我使用用户的公钥来加密用于加密和解密消息的对称密钥.

我分别在 Swift 和 Java(后端)中创建了两个框架来处理密钥生成、加密、解密等.我也对它们进行了测试,所以我 100% 一切都按预期工作.

但是,后端似乎无法识别从 iOS 传递过来的公钥的格式.双方都使用RSA,这是我在Swift中用来生成密钥的代码:

//私钥参数静态 let privateKeyParams: [String : Any] = [kSecAttrIsPermanent 作为字符串:true,kSecAttrApplicationTag as String: "..."//我这里有一个合适的唯一标签]//公钥参数静态让 publicKeyParams: [String : Any] = [kSecAttrIsPermanent 作为字符串:true,kSecAttrApplicationTag as String: "..."//我这里有一个合适的唯一标签]//密钥生成的全局参数静态 let keyCreationParameters: [String : Any] = [kSecAttrKeyType 作为字符串:kSecAttrKeyTypeRSA,kSecAttrKeySizeInBits 作为字符串:2048,kSecPublicKeyAttrs 作为字符串:publicKeyParams,kSecPrivateKeyAttrs 作为字符串:privateKeyParams]...var publicKey,privateKey:SecKey?让状态 = SecKeyGeneratePair(Constants.keyCreationParameters 作为 CFDictionary, &publicKey, &privateKey)

我使用镜面代码从钥匙串中读取钥匙.

这是我用来将公钥导出为 base64 字符串的一段代码:

extension SecKey {func asBase64() 抛出 ->细绳 {var dataPtr: CFTypeRef?让查询:[String:Any] = [kSecClass 作为字符串:kSecClassKey,kSecAttrApplicationTag as String: "...",//这里的唯一标签相同kSecAttrKeyType 作为字符串:kSecAttrKeyTypeRSA,kSecReturnData 作为字符串:kCFBooleanTrue]让结果 = SecItemCopyMatching(查询为 CFDictionary,&dataPtr)开关(结果,dataPtr){案例(errSecSuccess,.some(让数据))://转换为Base64字符串让 base64PublicKey = data.base64EncodedString(options: [])返回 base64PublicKey默认:抛出 CryptoError.keyConversionError}}}

在后端级别,我使用此 Java 代码将 base64 字符串转换为公钥:

public PublicKey publicKeyFrom(String data) throws NoSuchAlgorithmException, InvalidKeySpecException {byte[] publicBytes = Base64.decodeBase64(data);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);KeyFactory keyFactory = KeyFactory.getInstance("RSA");返回 keyFactory.generatePublic(keySpec);}

但这在最后一行失败了,有这个例外:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid 解析错误,不是序列

做了一些手动调试,发现公钥的格式不同——当我在iOS中生成一个密钥,然后导出为base 64时,它看起来是这样的:

<预> <代码> MIIBCgKCAQEA4M/bRDdH0f6qFIXxOg13RHka + g4Yv8u9PpPp1IR6pSwrM1aq8B6cyKRwnLe/MOkvODvDfJzvGXGQ01zSTxYWAW1B4uc/NCEemCmZqMosSB/VUJdNxxWtt2hJxpz06hAawqV + 6HmweAB2dUn9tDEsQLsNHdwYouOKpyRZGimcF9qRFn1RjR0Q54sUh1tQAj/EwmgY2S2bI5TqtZnZw7X7Waji7wWi6Gz88IkuzLAzB9VBNDeV1cfJFiWsZ/MIixSvhpW3dMNCrJShvBouIG8nS + vykBlbFVRGy3gJr8 + OcmIq5vuHVhqrWwHNOs + WR87K/qTFO/CB7MiyiIV1b1x5DQIDAQAB

总共 360 个字符,而在 Java 中做同样的事情(仍然使用 RSA)就像:

<预> <代码> MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAAnWO4BXUGP0qM3Op36YXkWNxb4I2pPZuZ7jJtfUO7v + IO1mq43WzNaxLqqLPkTnMrv2ACRDK55vin + leQlL1z0LzVxjtZ9F6pajQo1r7PqBlL5N8bzBFKpagEf0QfyHPw0/0kG9DMnvQ + Im881QyN2zdl33wp5Fi + jRT7cunFQIDAQAB

长度为 216 个字符.

我无法弄清楚出了什么问题 - 如果 iOS 处理不同键中的键,并且需要特殊处理才能与其他人交谈,显然我不会感到惊讶.

有什么想法吗?

解决方案

Java 需要以 DER 格式编码的公钥.不幸的是,iOS 不支持这种标准格式,需要额外的转换(我不知道这是否会在最新版本的 swift 中有所改进)

查看我的回答此处您可以使用 CryptoExportImportManager

func exportPublicKeyToDER(keyId:String) ->NSData?{让 publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)让 keyType = kSecAttrKeyTypeRSA让 keySize = 2048让 exportImportManager = CryptoExportImportManager()if let exportableDERKey = exportImportManager.exportPublicKeyToDER(publicKey, keyType: keyType as String, keySize: keySize) {返回可导出的DERKey} 别的 {返回零}}

TL;DR: RSA public key generated in iOS and stored in the keychain, exported as base64 and sent to a java backend, is not recognized.

I'm implementing a chat encryption feature in an iOS app, and I'm using symmetric + asymmetric keys to handle it.

Without going too much into details, at backend I use the user's public key to encrypt a symmetric key used to encrypt and decrypt messages.

I created two frameworks, respectively in Swift and in Java (backend) to handle key generation, encryption, decryption, etc. I also have tests for them, so I'm 100% everything works as expected.

However, it looks like the backend is unable to recognize the format of the public key passed from iOS. Using RSA both sides, this is the code I use in Swift to generate the key:

// private key parameters
static let privateKeyParams: [String : Any] = [
        kSecAttrIsPermanent as String: true,
        kSecAttrApplicationTag as String: "..." // I have a proper unique tag here
]

// public  key parameters
static let publicKeyParams: [String : Any] = [
        kSecAttrIsPermanent as String: true,
        kSecAttrApplicationTag as String: "..." // I have a proper unique tag here
]

// global parameters for our key generation
static let keyCreationParameters: [String : Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits as String: 2048,
        kSecPublicKeyAttrs as String: publicKeyParams,
        kSecPrivateKeyAttrs as String: privateKeyParams
]

...

var publicKey, privateKey: SecKey?
let status = SecKeyGeneratePair(Constants.keyCreationParameters as CFDictionary, &publicKey, &privateKey)

I use specular code to read the keys from the keychain.

This is the piece of code I use to export the public key as a base64 string:

extension SecKey {
  func asBase64() throws -> String {
    var dataPtr: CFTypeRef?
    let query: [String:Any] = [
      kSecClass as String: kSecClassKey,
      kSecAttrApplicationTag as String: "...", // Same unique tag here
      kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
      kSecReturnData as String: kCFBooleanTrue
    ]
    let result = SecItemCopyMatching(query as CFDictionary, &dataPtr)

    switch (result, dataPtr) {
    case (errSecSuccess, .some(let data)):
      // convert to Base64 string
      let base64PublicKey = data.base64EncodedString(options: [])
      return base64PublicKey
    default:
      throw CryptoError.keyConversionError
    }
  }
}

At backend level I use this Java code to convert the base64 string to a public key:

public PublicKey publicKeyFrom(String data) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] publicBytes = Base64.decodeBase64(data);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(keySpec);
}

But this fails at the last line, with this exception:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence

Doing some manual debugging, I noticed that the format of the public key is different - when I generate a key in iOS and then export as base 64, it looks like this:

MIIBCgKCAQEA4M/bRDdH0f6qFIXxOg13RHka+g4Yv8u9PpPp1IR6pSwrM1aq8B6cyKRwnLe/MOkvODvDfJzvGXGQ01zSTxYWAW1B4uc/NCEemCmZqMosSB/VUJdNxxWtt2hJxpz06hAawqV+6HmweAB2dUn9tDEsQLsNHdwYouOKpyRZGimcF9qRFn1RjR0Q54sUh1tQAj/EwmgY2S2bI5TqtZnZw7X7Waji7wWi6Gz88IkuzLAzB9VBNDeV1cfJFiWsZ/MIixSvhpW3dMNCrJShvBouIG8nS+vykBlbFVRGy3gJr8+OcmIq5vuHVhqrWwHNOs+WR87K/qTFO/CB7MiyiIV1b1x5DQIDAQAB

for a total of 360 characters, whereas doing the same in Java (still using RSA) it's like:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAAnWO4BXUGP0qM3Op36YXkWNxb4I2pPZuZ7jJtfUO7v+IO1mq43WzNaxLqqLPkTnMrv2ACRDK55vin+leQlL1z0LzVxjtZ9F6pajQo1r7PqBlL5N8bzBFKpagEf0QfyHPw0/0kG9DMnvQ+Im881QyN2zdl33wp5Fi+jRT7cunFQIDAQAB

with a length of 216 characters.

I'm unable to figure out what's wrong - apparently I wouldn't be surprised if iOS handles keys in a different key, and require special processing in order to talk with other folks.

Any idea?

解决方案

Java requires a public key encoded in DER format. Unfortunately iOS does not support this standard format and it is needed an additional conversion (I do not know if this will have improved in the latest versions of swift)

See my answer here You can convert the key using CryptoExportImportManager

func exportPublicKeyToDER(keyId:String) -> NSData?{

    let publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)
    let keyType = kSecAttrKeyTypeRSA
    let keySize = 2048
    let exportImportManager = CryptoExportImportManager()
    if let exportableDERKey = exportImportManager.exportPublicKeyToDER(publicKey, keyType: keyType as String, keySize: keySize) {
        return exportableDERKey
    } else {
        return nil
    }
}

这篇关于在 iOS/Swift 中创建并导出为 base64 的 RSA 公钥在 Java 中无法识别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆