iOS生成512位RSA公钥字符长度与Android/.NET不一致问题咨询
首先,你的问题核心在于不同平台生成的RSA公钥格式不同,并非iOS的RSA长度规则有差异,同时代码里也存在一个格式匹配的小错误,我们一步步拆解:
1. 公钥格式差异导致长度不同
Android和.NET平台默认生成的是SPKI(SubjectPublicKeyInfo)格式的公钥,这是一种包含算法元数据的完整公钥结构;而iOS的SecKeyCopyExternalRepresentation方法返回的RSA公钥是PKCS#1格式——仅包含RSA核心的模数(n)和公钥指数(e)的原始结构。
两种格式的字节数差异很明显:
- PKCS#1格式的512位RSA公钥原始数据约71字节,转Base64后约95字符;
- SPKI格式在PKCS#1基础上增加了算法标识符(RSA的OID和参数),原始数据约92字节,转Base64后约123字符。
加上头尾标识后,自然会出现你看到的长度差。
2. 代码中的格式错误
你代码里给生成的公钥加上了-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----的头尾,但这个标识对应的是SPKI格式,而你实际生成的是PKCS#1格式的公钥,属于格式不匹配。PKCS#1格式的RSA公钥应该用-----BEGIN RSA PUBLIC KEY-----和-----END RSA PUBLIC KEY-----作为头尾。
3. 解决方法:生成与其他平台一致的SPKI格式公钥
如果需要iOS生成的公钥和Android/.NET完全兼容(即SPKI格式),你需要把PKCS#1格式的公钥包装成SPKI结构。以下是修改后的代码示例,手动构建SPKI的ASN.1结构:
func generatePublicKey() -> String { var strKey : String = "" let publicKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"com.nichetech.DEMO.public".data(using: .utf8)! as NSObject] let privateKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"com.nichetech.DEMO.private".data(using: .utf8)! as NSObject] var keyPairAttr = [NSObject: NSObject]() keyPairAttr[kSecAttrKeyType] = kSecAttrKeyTypeRSA keyPairAttr[kSecAttrKeySizeInBits] = 512 as NSObject keyPairAttr[kSecPublicKeyAttrs] = publicKeyAttr as NSObject keyPairAttr[kSecPrivateKeyAttrs] = privateKeyAttr as NSObject var publicKey: SecKey? var privateKey: SecKey? let statusCode = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey) guard statusCode == errSecSuccess, let pubKey = publicKey else { return strKey } var error:Unmanaged<CFError>? if let pkcs1Data = SecKeyCopyExternalRepresentation(pubKey, &error) as Data? { // 构建SPKI格式的ASN.1结构 // 1. 算法标识符:RSA OID + NULL参数 let rsaOid = Data([0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00]) // 2. 把PKCS#1数据包装成BIT STRING let bitString = Data([0x03, UInt8(pkcs1Data.count + 1), 0x00]) + pkcs1Data // 3. 组合成完整的SPKI SEQUENCE let spkiData = Data([0x30, UInt8(rsaOid.count + bitString.count)]) + rsaOid + bitString // 转Base64并添加标准头尾 let b64Key = spkiData.base64EncodedString(options: .lineLength76Characters) let strPublicKey = "-----BEGIN PUBLIC KEY-----\n\(b64Key)\n-----END PUBLIC KEY-----" strKey = strPublicKey } return strKey }
这段代码会生成标准的SPKI格式公钥,长度会和Android/.NET平台的结果一致。
另外,如果你不需要跨平台兼容,只是修正现有格式的头尾,只需要把代码里的头尾改成-----BEGIN RSA PUBLIC KEY-----和-----END RSA PUBLIC KEY-----即可。
内容的提问来源于stack exchange,提问作者Dimple Shah

