【问题标题】:How to extract RSA parameters from iOS SecKeyRef?如何从 iOS SecKeyRef 中提取 RSA 参数?
【发布时间】:2014-03-14 15:10:04
【问题描述】:

我正在调用 iOS SecKeyGeneratePair 方法来生成 RSA 密钥对。现在我想从中提取 RSA 参数(公共和私有数据)。我怎样才能做到这一点?

我找到了CryptoExercise 示例,但我没有看到它实际上是在导出原始数据的密钥。此外,我真的很难理解它是 Objective-C。

我在 Xamarin.iOS (C#) 移动应用程序中执行此操作,因此我需要通过在 iOS 中调用必要的互操作 API 来用 C# 编写我的最终解决方案(就像我对 SecKeyGeneratePair 的调用一样)。

对于那些熟悉 Xamarin 的人,是的,我知道我可以使用 new RSACryptoServiceProvider() 来更轻松地完成它。当我使用本机 API 生成 RSA 密钥(甚至加密位本身)时,差异是 2-3 个数量级的性能改进。因此,虽然我必须使用原生 API 进行 RSA 工作,但我还需要获取原始数据,以便我可以跨平台。

【问题讨论】:

    标签: c# ios xamarin.ios cryptography rsa


    【解决方案1】:

    AFAIK 你不能直接提取参数。 Apple 开发者论坛上有一些关于此的讨论(您可能想查看)。可悲的是,Apple engineer answer 是假设(关于发布的代码)是实现细节,随时可能发生变化。

    您仍然可能能够使用可用的 API 间接执行此操作,例如将其导出为 PKCS#12,然后从中获取 RSA 参数。

    更新: 验证后您只能导入 PKCS#12 - 因此这无助于导出私钥。我又回到了没有支持的方法。除了使用托管代码之外,唯一安全的选择是包含本机代码(第三方库)来生成密钥对。

    我曾经尝试过类似的东西(它是导入,而不是导出),但是额外操作所需的时间使我的代码比仅使用 C# 慢。你的情况不同,所以 YMMV。

    注意:在我看来,钥匙串访问(不在进程中,已加密......)导致了大部分减速。一次性使用导入公钥是不值得的 - 但如果您多次(重新)使用它,那么(一次性)成本对您来说可能是可以接受的。

    【讨论】:

    • 导出到 PKCS#12 是完全可以接受的。我正在生成一个 4096 位密钥,对于托管实施,有时可能需要几个小时,所以我确信与本机密钥生成相比,这个过程和导出开销将是微不足道的。
    • 如果您有如何导出密钥的链接,请分享。谢谢
    • Apple 文档可能已过时,但记录的长度限制为 2048 位 - 现在我知道 4096 个有效(我的 5S 大约需要 1 分钟),但我不能说它是否适用于所有设备(或iOS7之前)developer.apple.com/library/mac/documentation/security/…
    • 苹果开发者论坛帖子的链接:devforums.apple.com/message/414922#414922
    • 谢谢。我可以只针对 iOS7。
    【解决方案2】:

    SecKeyGeneratePair 是一个被 SecKeyCreateRandomKey 取代的旧 API,它仅在 iOS 10 中可用。所以我将使用问题所涉及的旧函数来回答。我将坚持使用 Core Foundation API 来实现与 C# 的互操作性。

    您可以通过将公钥和私钥原始密钥数据添加到钥匙串并将其作为数据字节返回来导出。这是一个例子:

    //Convert key object into data
    SecKeyRef givenKey = publicOrPrivateFromSecKeyGeneratePair;
    static const uint8_t publicKeyIdentifier[] = "com.company.myTempRSAKey"; //make it unique per key
    CFDataRef publicTag = CFDataCreate(kCFAllocatorDefault, publicKeyIdentifier, sizeof(publicKeyIdentifier));
    if (publicTag)
    {
        OSStatus sanityCheck = noErr;
        CFDataRef publicKeyBits = NULL;
    
        //Create a dictionary info object describing that we are using RSA
        CFMutableDictionaryRef queryPublicKey = CFDictionaryCreateMutable(kCFAllocatorDefault, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        if (queryPublicKey)
        {
            CFDictionaryAddValue(queryPublicKey, kSecClass, kSecClassKey);
            CFDictionaryAddValue(queryPublicKey, kSecAttrApplicationTag, publicTag);
            CFDictionaryAddValue(queryPublicKey, kSecAttrKeyType, kSecAttrKeyTypeRSA);
            CFDictionaryAddValue(queryPublicKey, kSecAttrKeyClass, kSecAttrKeyClassPublic); //for public or:
            //CFDictionaryAddValue(queryPublicKey, kSecAttrKeyClass, kSecAttrKeyClassPrivate); //for private
            CFDictionaryAddValue(queryPublicKey, kSecAttrAccessible, kSecAttrAccessibleWhenUnlockedThisDeviceOnly); //other options...
    
            CFMutableDictionaryRef attributes = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 7, queryPublicKey);
            if (attributes)
            {
                // Temporarily add key to the Keychain, return as data:
                CFDictionaryAddValue(attributes, kSecValueRef, givenKey);
                CFDictionaryAddValue(attributes, kSecReturnData, kCFBooleanTrue);
    
                CFTypeRef result = NULL;
                sanityCheck = SecItemAdd(attributes, &result);
                if (sanityCheck == errSecSuccess)
                {
                    publicKeyBits = (CFDataRef)result; // Use the RAW key here
    
                    // Remove the temp key from the Keychain
                    sanityCheck = SecItemDelete(queryPublicKey);
                    if (sanityCheck != errSecSuccess)
                    {
                        //... Error deleting temporary public key from keychain
                    }
                }
                // else - failsafe code if key exists, try to delete first and then add item etc.
    
                CFRelease(attributes);
            }
            CFRelease(queryPublicKey);
        }
         CFRelease(publicTag);
    }
    

    这将为您提供原始数据。它是原始的,因为它缺少 Apple 以外的大多数系统所期望的标头。例如,RSA 公钥的 ASN.1 OID 值后跟一个终止 NULL 字节。

    //HEX: 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00
    
    /*
     SEQUENCE {
     OBJECTIDENTIFIER 1.2.840.113549.1.1.1 (rsaEncryption)
     NULL
     }
     */
    

    因此,如果您正在寻找原始数据,那么您就拥有它。如果您尝试通过“参数”从原始数据中提取模数和指数,您也可以这样做(例如,位流:mod + exp)。让我知道这是否是你所追求的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-20
      • 2015-07-09
      • 2011-08-22
      • 2015-08-25
      • 1970-01-01
      • 1970-01-01
      • 2011-09-11
      • 2013-05-20
      相关资源
      最近更新 更多