在Swift中,我通过在一些原始X509证书数据上调用SecKeyRef
来创建一个SecTrustCopyPublicKey
对象。这就是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
实际转换为字符串, NSData
或其他任何内容(这是我的目标,只是获取base64公钥)。
但是,我有一个函数,我可以给modulus
和exponent
,它只会计算公钥是什么。我通过传递上述SecKeyRef
中记录的数据对其进行了测试。
但不知怎的,我无法从SecKeyRef
对象访问这些属性(我只能在控制台中看到整个对象;例如,我无法做SecKeyRef.modulus
或任何事情那种,似乎)。
我的问题:如何访问SecKeyRef.modulus
,或者将此SecKeyRef
转换为NSData
或类似内容?谢谢
(更多信息)
我正在动态创建SecKeyRef
,通过此函数我有:
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
}
这样做是从证书中获取原始字节流(可以使用PKI从一个硬件广播),然后将其转换为SecKeyRef
。
(截至2015年1月7日对现有答案的评论)
这不起作用:
let mirror = Mirror(reflecting: mySecKeyObject)
for case let (label?, value) in mirror.children {
print (label, value)
}
这会在控制台中产生此输出:
Some <Raw SecKeyRef object>
不确定字符串&#34; Some&#34;装置
此外,mirror.descendant("exponent")
(或&#34;模数&#34;)会产生nil
,即使在控制台中打印原始对象时,我也可以清楚地看到这些属性存在,并且他们实际上是人口稠密的。
此外,如果可能,我希望避免必须保存到钥匙串,阅读为NSData
,然后从钥匙串中删除。如赏金说明中所述,如果这是唯一可行的方法,请引用权威参考。感谢您提供的所有答案。
答案 0 :(得分:7)
更新以下答案可能会导致您的应用因使用非公开API而被拒绝。
答案在于Apple的开源网站SecRSAKey.h文件(安全性是Apple开源的代码的一部分)。文件不大,除其他外,它声明了以下两个重要的功能:
CFDataRef SecKeyCopyModulus(SecKeyRef rsaPublicKey);
CFDataRef SecKeyCopyExponent(SecKeyRef rsaPublicKey);
您可以将这些功能添加到桥接标头中,以便能够从Swift中调用它们,同时执行此操作时,您可以从CFDataRef
切换到NSData*
两种类型的免费桥接:< / p>
NSData* SecKeyCopyModulus(SecKeyRef rsaPublicKey);
NSData* SecKeyCopyExponent(SecKeyRef rsaPublicKey);
Demo 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),并且看起来就像这两个函数一样存在于所有这些函数中。此外,由于Security
是操作系统的关键组成部分,因此苹果不太可能对其进行重大更改。
在iOS 8.1,iOS 9.2和OSX 10.10.5上测试过,代码适用于所有三个平台。
答案 1 :(得分:6)
我一直试图进行SSL公钥锁定。 API几乎不存在,我找到的解决方案是将它放在Keychain中,然后可以将其作为NSData检索(然后可以进行Base64编码)。这是可怕的,但是经过一天左右的研究后我才能找到唯一的东西(不需要在我的应用程序中捆绑OpenSSL)。
我将部分代码移植到Swift上,但我没有对它进行过多次测试,所以我不能100%确定它的工作原理:https://gist.github.com/chedabob/64a4cdc4a1194d815814
它基于这个Obj-C代码(我相信它在生产应用程序中有效):https://gist.github.com/chedabob/49eed109a3dfcad4bd41
答案 2 :(得分:6)
确实可以使用钥匙链和私有API来提取模数和指数。
(public but undocumented)函数SecKeyCopyAttributes
从CFDictionary
中提取SecKey
。
属性键的有用来源是SecItemConstants.c
检查此词典的内容,我们找到一个条目"v_Data" : <binary>
。
SEQUENCE {
modulus INTEGER,
publicExponent INTEGER
}
请注意,如果整数使用零字节填充,如果它们是正数且具有前导1位(以免将它们与双补码负数混淆),那么您可能会发现比预期更多的一个字节。如果发生这种情况,只需将其切掉。
您可以为此格式实现解析器,或者知道密钥大小,对提取进行硬编码。对于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)
}
(我最后编写了一个完整的ASN解析器,所以这段代码没有经过测试,要小心!)
请注意,您可以以非常相同的方式提取私有键的详细信息。使用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
}
手工解析可能是不明智的,因为任何整数都可能被填充。
Nota bene:如果在macOS上生成密钥,则公钥的格式不同;上面给出的结构包装如下:
SEQUENCE {
id OBJECTID,
PublicKey BITSTRING
}
位串是以上形式的DER编码ASN。
答案 3 :(得分:3)
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
*/
答案 4 :(得分:3)
我在一个废弃的项目中找到了一个单独的Obj-c重新实现ASN.1解析器,这似乎有效。问题是,它使用了大量的指针技巧,我不知道如何转换成Swift(甚至不确定其中有些可能)。应该可以在它周围创建一个快速友好的包装器,因为它唯一的输入是NSData。
网上的所有东西都在使用商店并在Keychain技巧中检索以获取酒吧密钥数据,甚至是真正流行的lib,如TrustKit。我在Apple docs on SecKeyRef中找到了根本原因的引用(我认为):
存储在钥匙串中的密钥的SecKeyRef对象可以是 安全地转换为SecKeychainItemRef以作为钥匙串进行操作 项目。另一方面,如果SecKeyRef没有存储在钥匙串中, 将对象转换为SecKeychainItemRef并将其传递给Keychain 服务函数返回错误。
由于目前iOS上没有SecCertificateCopyValues
,因此您只能解析证书数据或执行Keychain Item shuffle。
答案 5 :(得分:3)
我找到了如何获取SecKey
的数据。
let publicKey: SecKey = ...
let data = SecKeyCopyExternalRepresentation(publicKey, nil)
这似乎运作良好,我已经能够成功比较公钥。
这是在Swift 3(Xcode 8 beta 3)
中答案 6 :(得分:2)
您是否考虑过使用<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<h3>Red to White at 60%</h3>
<div class='menu-60'>Text</div>
<div class='menu-60'>Text</div>
<div class='menu-60'>Text</div>
<br/>
<h3>Red to White at 40%</h3>
<div class='menu-40'>Text</div>
<div class='menu-40'>Text</div>
<div class='menu-40'>Text</div>
<br/>
<h3>Red to White at 80%</h3>
<div class='menu-80'>Text</div>
<div class='menu-80'>Text</div>
<div class='menu-80'>Text</div>
?结果SecCertificateCopyData()
是免费桥接的,我认为。
请参阅https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/以查看API的相关文档。
答案 7 :(得分:1)
来自How do I encode an unmanaged<SecKey> to base64 to send to another server?:
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");
[编辑:镜子根据乔希不工作]
答案 8 :(得分:0)
我在stackoverflow中写了一篇关于其他答案的文章。目前我正在制作中使用它,但我很乐意使用另一种不需要写入钥匙串的解决方案。
- (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;
}