我能从一个SecKeyRef对象中得到模或者指数吗?

时间:2022-05-30 18:30:59

In Swift, I created a SecKeyRef object by calling SecTrustCopyPublicKey on some raw X509 certificate data. This is what this SecKeyRef object looks like.

在Swift中,我通过在一些原始的X509证书数据上调用SecTrustCopyPublicKey创建了一个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>)

Basically, this SecKeyRef object holds a whole bunch of information about the public key, but there seems to be no way to actually convert this SecKeyRef into a string, NSData, or anything else (this is my goal, is just to get a base64 public key).

基本上,这个SecKeyRef对象包含大量关于公钥的信息,但是似乎没有办法将这个SecKeyRef转换成字符串、NSData或其他任何东西(这是我的目标,只是获得一个base64公钥)。

However, I have a function that I can give a modulus and an exponent, and it will just calculate what the public key is. I've tested it by passing in the data that's logged from the above SecKeyRef.

但是,我有一个函数,我可以给出一个模数和一个指数,它会计算公钥是什么。我通过传递从上面的SecKeyRef记录的数据来测试它。

But somehow I can't access those properties from the SecKeyRef object (I can only see the whole object in the console; for example, I cannot do SecKeyRef.modulus or anything of the sort, it seems).

但是,我无法从SecKeyRef对象访问这些属性(我只能在控制台中看到整个对象;例如,我不能做SecKeyRef。模或者类似的东西)

My question: how can I access SecKeyRef.modulus, or alternatively, convert this SecKeyRef into NSData or something similar? Thanks

我的问题是:如何访问SecKeyRef。或者把这个SecKeyRef转换成NSData或者类似的东西?谢谢

Edit

(for more information)

(更多信息)

I am creating my SecKeyRef dynamically, through this function I have:

通过这个函数,我动态地创建了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
}

What that does is takes the raw byte stream from a certificate (which can be broadcasted from, say, a piece of hardware using PKI), and then turns that into a SecKeyRef.

它所做的就是从证书中获取原始的字节流(可以从一个使用PKI的硬件中进行广播),然后将其转换为一个SecKeyRef。

Edit 2

(comments on existing answers as of 7 January 2015)

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

This does not work:

这并不工作:

let mirror = Mirror(reflecting: mySecKeyObject)

for case let (label?, value) in mirror.children {
    print (label, value)
}

This results in this output in the console:

这导致控制台的输出:

Some <Raw SecKeyRef object>

Not sure what the string "Some" means.

不知道字符串“Some”是什么意思。

Additionally, mirror.descendant("exponent") (or "modulus") results in nil, even though when printing the raw object in the console, I can clearly see that those properties exist, and that they are in fact populated.

此外,mirror. descent(“指数”)(或“模数”)会导致nil,尽管在控制台中打印原始对象时,我可以清楚地看到这些属性存在,它们实际上是填充的。

Also, if at all possible, I would like to avoid having to save to the keychain, reading as NSData, and then deleting from the keychain. As stated in the bounty description, if this is the only way possible, please cite an authoritative reference. Thank you for all answers provided so far.

此外,如果可能的话,我希望避免必须保存到keychain,读取NSData,然后从keychain中删除。如赏金说明所述,如果这是唯一可能的方法,请引用权威的参考资料。感谢您迄今提供的所有答案。

9 个解决方案

#1


6  

I've been down the same path trying to do SSL Public Key Pinning. The API's are pretty much non-existent, and the solution I found was to put it in the Keychain which you can then retrieve as NSData (which can then be Base64 Encoded). It's horrible but the only thing I could find after a day or so of research (without resorting to bundling OpenSSL with my app).

我也曾尝试执行SSL公钥锁定。API几乎不存在,我找到的解决方案是将它放入Keychain中,然后您可以检索为NSData(然后可以对其进行Base64编码)。这很可怕,但经过一天左右的研究,我只能找到一件东西(不用借助我的应用绑定OpenSSL)。

I ported some of my code over to Swift, but I haven't tested it very much so I'm not 100% sure that it works: https://gist.github.com/chedabob/64a4cdc4a1194d815814

我将我的一些代码移植到Swift,但是我还没有对它进行测试,所以我不能100%确定它是否有效:https://gist.github.com/chedabob/64a4cdc4a1194d815814。

It's based off this Obj-C code (which I'm confident works as it's in a production app): https://gist.github.com/chedabob/49eed109a3dfcad4bd41

它基于这个object - c代码(我相信它在产品应用程序中工作):https://gist.github.com/chedabob/49eed109a3dfcad4bd41

#2


5  

The answer lies in the SecRSAKey.h file from Apple's opensource website (Security is part of the code that Apple opensourced). The file is not big, and among other stuff it declares the following two important functions:

答案就在后面。h文件来自苹果的opensource网站(安全是苹果opensource代码的一部分)。文件并不大,它声明了以下两个重要功能:

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

You can add those functions to your bridging header to be able to call them from Swift, also while doing this you can switch from CFDataRef to NSData* as the two types toll-free bridged:

您可以将这些功能添加到桥接头中,以便能够从Swift调用它们,同时还可以从CFDataRef切换到NSData*,因为这两种类型是免费桥接的:

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

Demo Swift usage:

演示迅速用法:

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

This is a private API though, and there might be a chance it will no longer be available at some point, however I looked over the versions of Security made public (http://www.opensource.apple.com/source/Security), and looks like the two functions are present in all of them. More, since Security is a critical component of the OS, it's unlikely Apple will do major changes over it.

尽管这是一个私有的API,而且可能在某些时候它将不再可用,但是我查看了公开的安全版本(http://www.opensource.apple.com/source/Security),看起来这两个函数都包含在其中。更重要的是,由于安全是操作系统的关键组成部分,苹果不太可能对其进行重大修改。

Tested on iOS 8.1, iOS 9.2, and OSX 10.10.5, and the code works on all three platforms.

在iOS 8.1、iOS 9.2和OSX 10.10.5上进行测试,代码可以在所有三个平台上运行。

#3


4  

It is indeed possible to extract modulus and exponent using neither keychains nor private API.

既不使用密钥链也不使用私有API提取模量和指数。

There is the (public but undocumented) function SecKeyCopyAttributes which extracts a CFDictionary from a SecKey. A useful source for attribute keys is SecItemConstants.c

有一个函数SecKeyCopyAttributes,它从SecKey中提取一个CFDictionary。属性键的一个有用来源是secitemconstant .c

Inspecting the content of this dictionary, we find an entry "v_Data" : <binary>. Its content is DER-encoded ASN for

检查本词典的内容,我们发现一个条目“v_Data”: <二进制> 。其内容为der编码的ASN . for

SEQUENCE {
    modulus           INTEGER, 
    publicExponent    INTEGER
}

Be aware that integers are padded with a zero byte if they are positive and have a leading 1-bit (so as not to confuse them with a two-complement negative number), so you may find one byte more than you expect. If that happens, just cut it away.

请注意,如果整数是正的,并且具有1位的前导(以免与双补负数混淆),则使用0字节进行填充,因此您可能会比预期的多找到一个字节。如果发生这种情况,就把它剪掉。

You can implement a parser for this format or, knowing your key size, hard-code the extraction. For 2048 bit keys (and 3-byte exponent), the format turns out to be:

您可以为这种格式实现解析器,或者了解密钥大小,硬编码提取。对于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>

For a total of 10 + 1? + 256 + 3 = 269 or 270 bytes.

总共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)
}

(I ended up writing a full ASN parser, so this code is not tested, beware!)

(我最终编写了一个完整的ASN解析器,所以这段代码没有经过测试,请注意!)


Note that you can extract details of private keys in very much the same way. Using DER terminology, this is the format of v_Data:

注意,您可以以非常相同的方式提取私钥的细节。使用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
 }

Parsing this by hand is probably ill-advised since any of the integers may have been padded.

手工解析这一点可能是不明智的,因为任何整数都可能被填充。


Nota bene: The format of the public key is different if the key has been generated on macOS; the structure given above is wrapped like so:

Nota bene:如果在macOS上生成了公钥,公钥的格式就不同;上面给出的结构是这样包装的:

SEQUENCE {
    id              OBJECTID,
    PublicKey       BITSTRING
}

The bit-string is DER-encoded ASN of the form above.

位字符串是上面表单的der编码ASN。

#4


3  

SecKeyRefis a struct so there is a chance that it can be reflected with Mirror() to retrieve the wanted values.

SecKeyRefis是一个结构体,因此有可能用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
*/

#5


3  

I've found a single Obj-c re-implementation of the ASN.1 parser in an abandoned project, that appears to work. Problem is, it uses a great deal of pointer tricks that I don't know how to translate into Swift (not even sure some of it is possible). It should be possible to create a swift friendly wrapper around it, since the only input it takes is the NSData.

我在一个废弃的项目中发现了ASN.1解析器的单一object -c重新实现,这似乎是可行的。问题是,它使用了大量的指针技巧,我不知道如何将其转换为Swift(甚至不确定其中的一些是否可能)。应该可以在它周围创建一个快速友好的包装器,因为它所需要的唯一输入是NSData。

Everything on the net is using the store and retrieve in the Keychain trick to get to the pub key data, even really popular libs like TrustKit. I found reference in the Apple docs on SecKeyRef to the root cause (I think):

网络上的所有东西都在使用存储和检索键链技巧来获取pub键数据,甚至是真正流行的libs,比如TrustKit。我在SecKeyRef上的苹果文档中找到了根本原因(我认为):

A SecKeyRef object for a key that is stored in a keychain can be safely cast to a SecKeychainItemRef for manipulation as a keychain item. On the other hand, if the SecKeyRef is not stored in a keychain, casting the object to a SecKeychainItemRef and passing it to Keychain Services functions returns errors.

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

Since SecCertificateCopyValues isn't available on iOS at this time, you're limited to either parsing the certificate data, or doing the Keychain Item shuffle.

由于seccertification ecopyvalues目前在iOS上不可用,因此您只能解析证书数据,或者执行Keychain项洗牌。

#6


2  

Did you think about using SecCertificateCopyData()? The resulting CFData is toll-Free bridged, I think.

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

Refer to https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/ to see the relevant documentation of the API.

请参考https://developer.apple.com/library/ios/documentation/security/reference/fkeytrustservices/以查看API的相关文档。

#7


1  

From How do I encode an unmanaged<SecKey> to base64 to send to another server? :

如何将非托管的 编码到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
}

But if you want to avoid adding to keychain, you can use Mirror:

但是如果你想避免添加到keychain,你可以使用Mirror:

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

[edit: Mirror not working according to Josh]

[编辑:镜像不按Josh的方式工作]

#8


1  

I found how to get data for a SecKey.

我找到了如何获取SecKey的数据。

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

This seems to work well and I have been able to successfully compare public keys.

这似乎很有效,我已经成功地比较了公钥。

This is in Swift 3 (Xcode 8 beta 3)

这是Swift 3 (Xcode 8 beta 3)

#9


0  

I wrote this one base on some other's answer in *. Currently I am using it in my production but I am happy to use another solution that doesn't require to write into keychain.

我是根据*上其他人的答案写的。目前我正在生产中使用它,但是我很高兴使用另一个不需要写入keychain的解决方案。

- (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;
}

#1


6  

I've been down the same path trying to do SSL Public Key Pinning. The API's are pretty much non-existent, and the solution I found was to put it in the Keychain which you can then retrieve as NSData (which can then be Base64 Encoded). It's horrible but the only thing I could find after a day or so of research (without resorting to bundling OpenSSL with my app).

我也曾尝试执行SSL公钥锁定。API几乎不存在,我找到的解决方案是将它放入Keychain中,然后您可以检索为NSData(然后可以对其进行Base64编码)。这很可怕,但经过一天左右的研究,我只能找到一件东西(不用借助我的应用绑定OpenSSL)。

I ported some of my code over to Swift, but I haven't tested it very much so I'm not 100% sure that it works: https://gist.github.com/chedabob/64a4cdc4a1194d815814

我将我的一些代码移植到Swift,但是我还没有对它进行测试,所以我不能100%确定它是否有效:https://gist.github.com/chedabob/64a4cdc4a1194d815814。

It's based off this Obj-C code (which I'm confident works as it's in a production app): https://gist.github.com/chedabob/49eed109a3dfcad4bd41

它基于这个object - c代码(我相信它在产品应用程序中工作):https://gist.github.com/chedabob/49eed109a3dfcad4bd41

#2


5  

The answer lies in the SecRSAKey.h file from Apple's opensource website (Security is part of the code that Apple opensourced). The file is not big, and among other stuff it declares the following two important functions:

答案就在后面。h文件来自苹果的opensource网站(安全是苹果opensource代码的一部分)。文件并不大,它声明了以下两个重要功能:

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

You can add those functions to your bridging header to be able to call them from Swift, also while doing this you can switch from CFDataRef to NSData* as the two types toll-free bridged:

您可以将这些功能添加到桥接头中,以便能够从Swift调用它们,同时还可以从CFDataRef切换到NSData*,因为这两种类型是免费桥接的:

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

Demo Swift usage:

演示迅速用法:

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

This is a private API though, and there might be a chance it will no longer be available at some point, however I looked over the versions of Security made public (http://www.opensource.apple.com/source/Security), and looks like the two functions are present in all of them. More, since Security is a critical component of the OS, it's unlikely Apple will do major changes over it.

尽管这是一个私有的API,而且可能在某些时候它将不再可用,但是我查看了公开的安全版本(http://www.opensource.apple.com/source/Security),看起来这两个函数都包含在其中。更重要的是,由于安全是操作系统的关键组成部分,苹果不太可能对其进行重大修改。

Tested on iOS 8.1, iOS 9.2, and OSX 10.10.5, and the code works on all three platforms.

在iOS 8.1、iOS 9.2和OSX 10.10.5上进行测试,代码可以在所有三个平台上运行。

#3


4  

It is indeed possible to extract modulus and exponent using neither keychains nor private API.

既不使用密钥链也不使用私有API提取模量和指数。

There is the (public but undocumented) function SecKeyCopyAttributes which extracts a CFDictionary from a SecKey. A useful source for attribute keys is SecItemConstants.c

有一个函数SecKeyCopyAttributes,它从SecKey中提取一个CFDictionary。属性键的一个有用来源是secitemconstant .c

Inspecting the content of this dictionary, we find an entry "v_Data" : <binary>. Its content is DER-encoded ASN for

检查本词典的内容,我们发现一个条目“v_Data”: <二进制> 。其内容为der编码的ASN . for

SEQUENCE {
    modulus           INTEGER, 
    publicExponent    INTEGER
}

Be aware that integers are padded with a zero byte if they are positive and have a leading 1-bit (so as not to confuse them with a two-complement negative number), so you may find one byte more than you expect. If that happens, just cut it away.

请注意,如果整数是正的,并且具有1位的前导(以免与双补负数混淆),则使用0字节进行填充,因此您可能会比预期的多找到一个字节。如果发生这种情况,就把它剪掉。

You can implement a parser for this format or, knowing your key size, hard-code the extraction. For 2048 bit keys (and 3-byte exponent), the format turns out to be:

您可以为这种格式实现解析器,或者了解密钥大小,硬编码提取。对于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>

For a total of 10 + 1? + 256 + 3 = 269 or 270 bytes.

总共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)
}

(I ended up writing a full ASN parser, so this code is not tested, beware!)

(我最终编写了一个完整的ASN解析器,所以这段代码没有经过测试,请注意!)


Note that you can extract details of private keys in very much the same way. Using DER terminology, this is the format of v_Data:

注意,您可以以非常相同的方式提取私钥的细节。使用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
 }

Parsing this by hand is probably ill-advised since any of the integers may have been padded.

手工解析这一点可能是不明智的,因为任何整数都可能被填充。


Nota bene: The format of the public key is different if the key has been generated on macOS; the structure given above is wrapped like so:

Nota bene:如果在macOS上生成了公钥,公钥的格式就不同;上面给出的结构是这样包装的:

SEQUENCE {
    id              OBJECTID,
    PublicKey       BITSTRING
}

The bit-string is DER-encoded ASN of the form above.

位字符串是上面表单的der编码ASN。

#4


3  

SecKeyRefis a struct so there is a chance that it can be reflected with Mirror() to retrieve the wanted values.

SecKeyRefis是一个结构体,因此有可能用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
*/

#5


3  

I've found a single Obj-c re-implementation of the ASN.1 parser in an abandoned project, that appears to work. Problem is, it uses a great deal of pointer tricks that I don't know how to translate into Swift (not even sure some of it is possible). It should be possible to create a swift friendly wrapper around it, since the only input it takes is the NSData.

我在一个废弃的项目中发现了ASN.1解析器的单一object -c重新实现,这似乎是可行的。问题是,它使用了大量的指针技巧,我不知道如何将其转换为Swift(甚至不确定其中的一些是否可能)。应该可以在它周围创建一个快速友好的包装器,因为它所需要的唯一输入是NSData。

Everything on the net is using the store and retrieve in the Keychain trick to get to the pub key data, even really popular libs like TrustKit. I found reference in the Apple docs on SecKeyRef to the root cause (I think):

网络上的所有东西都在使用存储和检索键链技巧来获取pub键数据,甚至是真正流行的libs,比如TrustKit。我在SecKeyRef上的苹果文档中找到了根本原因(我认为):

A SecKeyRef object for a key that is stored in a keychain can be safely cast to a SecKeychainItemRef for manipulation as a keychain item. On the other hand, if the SecKeyRef is not stored in a keychain, casting the object to a SecKeychainItemRef and passing it to Keychain Services functions returns errors.

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

Since SecCertificateCopyValues isn't available on iOS at this time, you're limited to either parsing the certificate data, or doing the Keychain Item shuffle.

由于seccertification ecopyvalues目前在iOS上不可用,因此您只能解析证书数据,或者执行Keychain项洗牌。

#6


2  

Did you think about using SecCertificateCopyData()? The resulting CFData is toll-Free bridged, I think.

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

Refer to https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/ to see the relevant documentation of the API.

请参考https://developer.apple.com/library/ios/documentation/security/reference/fkeytrustservices/以查看API的相关文档。

#7


1  

From How do I encode an unmanaged<SecKey> to base64 to send to another server? :

如何将非托管的 编码到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
}

But if you want to avoid adding to keychain, you can use Mirror:

但是如果你想避免添加到keychain,你可以使用Mirror:

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

[edit: Mirror not working according to Josh]

[编辑:镜像不按Josh的方式工作]

#8


1  

I found how to get data for a SecKey.

我找到了如何获取SecKey的数据。

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

This seems to work well and I have been able to successfully compare public keys.

这似乎很有效,我已经成功地比较了公钥。

This is in Swift 3 (Xcode 8 beta 3)

这是Swift 3 (Xcode 8 beta 3)

#9


0  

I wrote this one base on some other's answer in *. Currently I am using it in my production but I am happy to use another solution that doesn't require to write into keychain.

我是根据*上其他人的答案写的。目前我正在生产中使用它,但是我很高兴使用另一个不需要写入keychain的解决方案。

- (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;
}