我有两个NSString对象,它们表示一个RSA公私密钥对(不是由SecKeyCreatePair生成的,而是由外部加密库生成的)。如何从这些NSString对象创建SecKeyRef对象( SecKeyDecrypt/Encrypt方法需要这些对象)?
我需要先把它们导入到钥匙链中吗?如果是这样的话,是怎么做的?
谢谢!
发布于 2014-01-11 05:55:17
答案是使用正确的标志集调用SecItemAdd。请参阅:http://hg.mozilla.org/services/fx-home/file/tip/Sources/NetworkAndStorage/CryptoUtils.m#l931
发布于 2014-05-20 12:58:02
所以在iOS中,钥匙链被放在沙盒中。这意味着,除非您另行指定,否则您放入密钥链的任何内容都只能由您的应用程序和您的应用程序访问。您必须在项目设置中的Capabilities下启用Keychain共享。
既然这样就没问题了,你当然可以导入数据了。由于它们是NSString对象,您必须首先将其转换为NSData对象才能正确地导入它们。最有可能的是,它们是用Base64编码的,所以你必须颠倒过来:
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];现在,您可以使用此方法将密钥保存到密钥链并获取SecKeyRef:
/**
* key: the data you're importing
* keySize: the length of the key (512, 1024, 2048)
* isPrivate: is this a private key or public key?
*/
- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *saveDict = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecValueData : key,
(__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
};
SecKeyRef savedKeyRef = NULL;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKeyRef);
if (sanityCheck != errSecSuccess) {
LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
}
return savedKeyRef;
}稍后,如果您想要从密钥链中检索SecKeyRef,可以使用以下命令:
- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
if (privateKeyRef != NULL) {
// already exists in memory, return
return privateKeyRef;
}
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
if (publicKeyRef != NULL) {
// already exists in memory, return
return publicKeyRef;
}
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *queryDict = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
};
SecKeyRef keyReference = NULL;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
if (sanityCheck != errSecSuccess) {
NSLog(@"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
}
if (isPrivate) {
privateKeyRef = keyReference;
}
else {
publicKeyRef = keyReference;
}
return keyReference;
}发布于 2014-10-02 22:03:17
编辑:使用下面的方法,我们能够导入最大尺寸为4096的密钥。任何大于这个值的RSA密钥大小似乎都会被密钥链拒绝。我们得到了一个成功的状态,但是我们没有得到对密钥的引用。
关于导入RSA私钥/公钥的快速说明。在我的例子中,我需要导入由OpenSSL生成的私钥。
This project做了我想做的大部分事情,就是把它放到密钥链中。正如您所看到的,它只有一个keydata,您可以将密钥数据放入其中,然后keychain根据密钥计算出块大小等。密钥链支持ASN.1编码的密钥。
将密钥导出到文件时,该文件最有可能是PEM文件。PEM文件只是一个base64编码的DER结构。DER结构是一种广义结构,但在OpenSSL的情况下,它通常是ASN.1编码的私钥或公钥。
ASN.1结构在here中显示得非常好。请先阅读并了解如何读取ASN.1结构,然后再尝试摆弄此结构,否则导入其他大小的密钥将失败。
我显然没有足够的“名气”来发布超过2个链接。因此,对于下面的示例,粘贴base64信息(除了- BEGIN *键-和-END*键-之外的所有内容: lapo.it/asn1js。
如果你看一下我链接的iOS项目,你会看到它们包含了样例关键字。将私钥粘贴到ASN.1解码器中。您会注意到,您有一个序列标记,后面跟着几个整数值。
现在粘贴公钥。您将注意到,公钥与私钥有两条相同的信息。模数和指数。在私钥中,这是第二个和第三个整数值。它的顶部也有一些信息。它有两个额外的序列,一个对象ID,NULL和位串标签。
您还会注意到,在项目中,他调用了一个特殊函数来处理该公钥。它所做的就是剥离所有的头信息,直到它到达最里面的序列标签。在这一点上,他将其完全视为私钥,并可以将其放入密钥链。
为什么要这样做,而不是另一个呢?查看页眉和页脚文本。私钥表示-开始RSA私钥-,公钥表示-开始公钥-。您将在公钥中看到的对象ID为: 1.2.840.113549.1.1.1。这是一个ID,它是将所包含的密钥标识为RSA类型密钥的静态标记。
由于私钥在前导中具有RSA,因此假设它是RSA密钥,并且不需要报头ASN.1信息来识别密钥。公钥只是一个通用密钥,因此需要一个头来标识它是什么类型的密钥。
密钥链不会导入具有此ASN.1标头的RSA密钥。你需要把它一直剥离到最后一个序列。在这一点上,您可以将其放入keychain,并且keychain能够派生出块大小和其他密钥属性。
因此,如果BEGIN RSA私有密钥存在,则不需要进行剥离。如果是-- BEGIN PRIVATE KEY -,则需要在将其放入keychain之前去掉这些初始头。
在我的例子中,我还需要公钥。一旦我们成功地插入私钥,我们就无法从keychain中获得它(我们可能遗漏了一些东西),所以我们实际上从私钥创建了一个ASN.1公钥,并将其导入keycahin中。
在私钥中(在ASN.1头部剥离之后),你将有一个序列标签,后面跟着3个整数标签(后面还有更多的整数,但前3个是我们所关心的)。
第一个是版本标签。第二个是模数,第三个是公共指数。
查看公钥(在ASN.1报头剥离之后),您会看到序列后面跟着2个整数。你猜对了,这是私钥的模数和公钥指数。
所以你需要做的就是:
获取的指数数据
这就是从您导入的私钥创建公钥所需做的全部工作。似乎没有太多关于导入RSA密钥的信息,你不是在设备上生成的,我听说在设备上生成的密钥不包含这些ASN.1头,但我从未尝试过。我们的密钥很大,而且生成设备的时间太长了。我曾经找到的唯一选择是使用OpenSSL,在那里你必须为iOS编译你自己的。我更愿意在可能的情况下使用安全框架。
我仍然是iOS开发的新手,我相信有人知道一个简单的函数,它可以完成我找不到的所有这些功能,所以我就去找了。这似乎可以很好地工作,直到有更简单的API可用来处理密钥。
最后要注意的一点是:项目中包含的私钥有一个位字符串标记,但我从OpenSSL生成的私钥导入的私钥有标记八位字节字符串。
https://stackoverflow.com/questions/792097
复制相似问题