我可以从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键)。
但是,我有一个函数,我可以给一个modulus
和exponent
,它只会计算公钥是什么。 我已经通过传入从上面的SecKeyRef
logging的数据来testing它。
但不知何故,我不能从SecKeyRef
对象访问这些属性(我只能看到控制台中的整个对象;例如,我不能做SecKeyRef.modulus
或类似的东西,似乎) 。
我的问题:我如何访问SecKeyRef.modulus
,或者,将此SecKeyRef
转换为NSData
或类似的东西? 谢谢
编辑
(了解更多信息)
我正在创build我的SecKeyRef
dynamic,通过这个函数我有:
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
免费桥接,我想。
使用Keychain 和私有API都可以提取模数和指数。
有( public但未logging的 )函数SecKeyCopyAttributes
从SecKey
提取一个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; }