我在调用iOS的 SecKeyGeneratePair
方法来生成一个RSA密钥对。现在我想从中提取RSA参数(包括公共数据和私有数据)。我如何才能做到这一点?
我已经找到了 密码练习 样例,但我没有看到它真的将键导出为原始数据。此外,它是Objective-C,我真的很难理解。
我在Xamarin.iOS (C#)移动应用中做这件事,所以我需要我的最终解决方案是用C#编写的,通过调用必要的interop APIs到iOS中(就像我调用的 SecKeyGeneratePair
).
对于那些熟悉Xamarin的人,是的,我知道我可以使用 new RSACryptoServiceProvider()
来做得更容易。当我使用原生API进行RSA密钥生成(甚至是加密位本身)时,差异是2-3个数量级的perf改进。所以,虽然我必须使用原生API来完成RSA工作,但我也需要把原始数据弄出来,这样才能跨平台。
AFAIK你不能直接提取参数。在Apple开发者论坛上有一些关于这个问题的讨论(你可能想看看)。遗憾的是 苹果工程师回答 是假设(关于发布的代码)是实现细节,随时可能改变。
但你 可能 能够使用可用的API间接地做到这一点,例如,将其导出为PKCS#12,然后从中获取RSA参数。
更新。 经过验证,你只能 输入 PKCS#12 - 所以这不会有任何帮助。输出 私钥。我又回到了 无路可走. 除了使用托管代码之外,我能想到的唯一安全的选择是包含本地代码(第三方库)来生成密钥对。
我曾经尝试过类似的方法(是导入,而不是导出),但额外操作所需的时间使我的代码比只使用C#更慢。你的情况不同,所以YMMV。
注:在我看来,似乎是钥匙链的访问(是在进程外,加密的......)造成了大部分的减速。为了一次性使用而导入一个公钥是不值得的--但如果你多次(重新)使用它,那么(一次性)成本可能是你可以接受的。
该 SecKeyGeneratePair
是一个旧的API,被 SecKeyCreateRandomKey
那是iOS 10才有的功能。所以我将使用问题中问到的旧函数来回答。为了与C#的互操作性,我将坚持使用Core Foundation APIs。
你可以通过将公钥和私钥的原始数据添加到钥匙链中并以数据字节的形式返回,从而导出公钥和私钥数据。下面是一个例子。
//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)。如果这是你想要的,请告诉我。