我可以从Swift中的SecKeyRef对象获取模数或指数吗?

在Swift中,我通过调用一些原始X509证书数据上的SecTrustCopyPublicKey创build了一个SecKeyRef对象。 这就是这个SecKeyRef对象的样子。

 Optional(<SecKeyRef algorithm id: 1, key type: RSAPublicKey, version: 3, block size: 2048 bits, exponent: {hex: 10001, decimal: 65537}, modulus: <omitted a bunch of hex data>, addr: 0xsomeaddresshere>) 

基本上,这个SecKeyRef对象包含了大量有关公钥的信息,但似乎没有办法将SecKeyRef实际转换为string, NSData或其他任何东西(这是我的目标,只是为了获得一个base64 public键)。

但是,我有一个函数,我可以给一个modulusexponent ,它只会计算公钥是什么。 我已经通过传入从上面的SecKeyReflogging的数据来testing它。

但不知何故,我不能从SecKeyRef对象访问这些属性(我只能看到控制台中的整个对象;例如,我不能做SecKeyRef.modulus或类似的东西,似乎)

我的问题:我如何访问SecKeyRef.modulus ,或者,将此SecKeyRef转换为NSData或类似的东西? 谢谢

编辑

(了解更多信息)

我正在创build我的SecKeyRefdynamic,通过这个函数我有:

 func bytesToPublicKey(certData: NSData) -> SecKeyRef? { guard let certRef = SecCertificateCreateWithData(nil, certData) else { return nil } var secTrust: SecTrustRef? let secTrustStatus = SecTrustCreateWithCertificates(certRef, nil, &secTrust) if secTrustStatus != errSecSuccess { return nil } var resultType: SecTrustResultType = UInt32(0) // result will be ignored. let evaluateStatus = SecTrustEvaluate(secTrust!, &resultType) if evaluateStatus != errSecSuccess { return nil } let publicKeyRef = SecTrustCopyPublicKey(secTrust!) return publicKeyRef } 

这样做是从证书的原始字节stream(可以从使用PKI的硬件中广播),然后将其转换为SecKeyRef

编辑2

(截至2015年1月7日对现有答复的评论)

这不起作用:

 let mirror = Mirror(reflecting: mySecKeyObject) for case let (label?, value) in mirror.children { print (label, value) } 

这导致在控制台中输出:

 Some <Raw SecKeyRef object> 

不知道string“Some”是什么意思。

此外, mirror.descendant("exponent") (或“modulus”)的结果nil ,即使在控制台中打印原始对象时,我也可以清楚地看到这些属性存在,并且实际上已经填充。

另外,如果可能, 我想避免保存到钥匙串,读取NSData ,然后从钥匙串中删除 。 正如在赏金说明中所说,如果这是唯一可能的方式,请引用权威的参考。 感谢您提供迄今为止提供的所有答案。

我一直在尝试做SSL公钥固定。 API几乎不存在,我find的解决scheme是把它放在钥匙串,然后你可以检索NSData(然后可以是Base64编码)。 这是非常可怕的,但是经过一天左右的研究之后我才能find(而不是诉诸捆绑OpenSSL与我的应用程序)。

我把我的一些代码移植到了Swift中,但是我没有对它进行太多的testing,所以我不能100%确定它的工作原理: https : //gist.github.com/chedabob/64a4cdc4a1194d815814

它基于这个Obj-C代码(我相信它可以在生产应用程序中使用): https : //gist.github.com/chedabob/49eed109a3dfcad4bd41

答案在于苹果开源网站的SecRSAKey.h文件(安全是苹果开源的代码的一部分)。 该文件不大,其他的东西,它声明以下两个重要的function:

 CFDataRef SecKeyCopyModulus(SecKeyRef rsaPublicKey); CFDataRef SecKeyCopyExponent(SecKeyRef rsaPublicKey); 

您可以将这些函数添加到您的桥接头,以便能够从Swift调用它们,同时这样做,您可以从CFDataRef切换到NSData*作为两种免费桥接模式:

 NSData* SecKeyCopyModulus(SecKeyRef rsaPublicKey); NSData* SecKeyCopyExponent(SecKeyRef rsaPublicKey); 

演示Swift用法:

 let key = bytesToPublicKey(keyData) let modulus = SecKeyCopyModulus(key) let exponent = SecKeyCopyExponent(key) print(modulus, exponent) 

虽然这是一个私有的API,但有可能在某些时候不可用,但是我查看了Security公布的版本( http://www.opensource.apple.com/source/Security ) ,而且看起来这两种function都存在于其中。 而且,由于Security是操作系统的重要组成部分,所以苹果公司不太可能对其做出重大改变。

在iOS 8.1,iOS 9.2和OSX 10.10.5上testing,代码适用于所有三个平台。

SecKeyRef是一个结构体,所以有可能用Mirror()来检索想要的值。

 struct myStruct { let firstString = "FirstValue" let secondString = "SecondValue"} let testStruct = myStruct() let mirror = Mirror(reflecting: testStruct) for case let (label?, value) in mirror.children { print (label, value) } /** Prints: firstString FirstValue secondString SecondValue */ 

我发现一个Obj-c在废弃的项目中重新实现了ASN.1parsing器,看起来可行。 问题是,它使用了大量的指针技巧,我不知道如何翻译成Swift(甚至不知道它有些可能)。 应该可以创build一个快速友好的包装器,因为唯一的input是NSData。

networking上的所有东西都在使用存储,并在Keychain技巧中检索,以获取pub密钥数据,甚至是真正stream行的库,如TrustKit 。 我发现在SecKeyRef的苹果文档的根源(我认为)的参考:

存储在钥匙串中的钥匙的SecKeyRef对象可以安全地转换为SecKeychainItemRef作为钥匙链项目进行操作。 另一方面,如果SecKeyRef未存储在钥匙串中,则将该对象强制转换为SecKeychainItemRef并将其传递给钥匙串服务函数将返回错误。

由于SecCertificateCopyValues目前在iOS上不可用,因此您仅限于parsing证书数据或执行Keychain Item shuffle。

你有没有考虑使用SecCertificateCopyData() ? 由此产生的CFData免费桥接,我想。

请参阅https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/以查看API的相关文档&#x3002;

使用Keychain 私有API都可以提取模数和指数。

有( public但未logging的 )函数SecKeyCopyAttributesSecKey提取一个CFDictionary 。 属性键的一个有用的来源是SecItemConstants.c

检查这个词典的内容,我们find一个条目"v_Data" : <binary> 。 它的内容是DER编码的ASN

 SEQUENCE { modulus INTEGER, publicExponent INTEGER } 

请注意,如果整数是正数,并且有一个前导1位(以免将它们与二进制补码负数混淆),那么整数用零字节填充,因此您可能会发现比您期望的更多的一个字节。 如果发生这种情况,就把它切下来。

您可以实现此格式的parsing器,或者知道您的密钥大小,对解压缩进行硬编码。 对于2048位密钥(和3字节指数),格式结果是:

 30|82010(a|0) # Sequence of length 0x010(a|0) 02|82010(1|0) # Integer of length 0x010(1|0) (00)?<modulus> 02|03 # Integer of length 0x03 <exponent> 

总共10 + 1? + 256 + 3 = 269或270字节。

 import Foundation extension String: Error {} func parsePublicSecKey(publicKey: SecKey) -> (mod: Data, exp: Data) { let pubAttributes = SecKeyCopyAttributes(publicKey) as! [String: Any] // Check that this is really an RSA key guard Int(pubAttributes[kSecAttrKeyType as String] as! String) == Int(kSecAttrKeyTypeRSA as String) else { throw "Tried to parse non-RSA key as RSA key" } // Check that this is really a public key guard Int(pubAttributes[kSecAttrKeyClass as String] as! String) == Int(kSecAttrKeyClassPublic as String) else { throw "Tried to parse non-public key as public key" } let keySize = pubAttributes[kSecAttrKeySizeInBits as String] as! Int // Extract values let pubData = pubAttributes[kSecValueData as String] as! Data var modulus = pubData.subdata(in: 8..<(pubData.count - 5)) let exponent = pubData.subdata(in: (pubData.count - 3)..<pubData.count) if modulus.count > keySize / 8 { // --> 257 bytes modulus.removeFirst(1) } return (mod: modulus, exp: exponent) } 

(我写了一个完整的ASNparsing器,所以这个代码没有经过testing,要小心!)


请注意,您可以以非常相似的方式提取私钥的详细信息。 使用DER术语,这是v_Data的格式:

 PrivateKey ::= SEQUENCE { version INTEGER, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) (dmp1) exponent2 INTEGER, -- d mod (q-1) (dmq1) coefficient INTEGER, -- (inverse of q) mod p (coeff) otherPrimeInfos OtherPrimeInfos OPTIONAL } 

由于任何整数都可能已被填充,因此可能不太合适。


注意:如果在macOS上生成密钥,则公钥的格式不同; 上面给出的结构是这样包装的:

 SEQUENCE { id OBJECTID, PublicKey BITSTRING } 

位串是以上forms的DER编码的ASN。

从我如何编码非托pipe的<SecKey>到base64发送到另一台服务器? :

 func convertSecKeyToBase64(inputKey: SecKey) ->String? { // Add to keychain let tempTag = "net.example." + NSUUID().UUIDString let addParameters :[String:AnyObject] = [ String(kSecClass): kSecClassKey, String(kSecAttrApplicationTag): tempTag, String(kSecAttrKeyType): kSecAttrKeyTypeRSA, String(kSecValueRef): inputKey, String(kSecReturnData):kCFBooleanTrue ] var result: String? var keyPtr: AnyObject? if (SecItemAdd(addParameters, &keyPtr) == noErr) { let data = keyPtr! as! NSData result = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) } // Remove from Keychain: SecItemDelete(addParameters) return result } 

但是,如果你想避免添加钥匙串,你可以使用镜像:

 let mirrorKey = Mirror(reflecting: secKey) let exponent = mirrorKey.descendant("exponent") let modulus = mirrorKey.descendant("modulus"); 

[编辑:镜子不按Josh工作]

我发现如何获取SecKey数据。

 let publicKey: SecKey = ... let data = SecKeyCopyExternalRepresentation(publicKey, nil) 

这似乎工作得很好,我已经能够成功比较公钥。

这是在Swift 3(Xcode 8 beta 3)

我在这个基础上写了一个基于其他人的答案在stackoverflow。 目前我正在使用它在我的生产,但我很高兴使用另一个不需要写入钥匙链的解决scheme。

 - (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey host:(NSString*)host { NSString *tag = [NSString stringWithFormat:@"%@.%@",[[NSBundle mainBundle] bundleIdentifier], host]; const char* publicKeyIdentifier = [tag cStringUsingEncoding:NSUTF8StringEncoding]; NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:strlen(publicKeyIdentifier) * sizeof(char)]; OSStatus sanityCheck = noErr; // NSData * publicKeyBits = nil; CFTypeRef publicKeyBits; NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; // Set the public key query dictionary. [queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; [queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag]; [queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData]; [queryPublicKey setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef]; // Get the key bits. NSData *data = nil; sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, &publicKeyBits); if (sanityCheck == errSecSuccess) { data = CFBridgingRelease(publicKeyBits); //I don't want to leak this information (void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey); }else { sanityCheck = SecItemAdd((CFDictionaryRef)queryPublicKey, &publicKeyBits); if (sanityCheck == errSecSuccess) { data = CFBridgingRelease(publicKeyBits); (void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey); } } return data; }