web-dev-qa-db-ja.com

SwiftのSecKeyRefオブジェクトから係数または指数を取得できますか?

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オブジェクトは一連の情報を保持していますabout公開鍵ですが、実際にこのSecKeyRefを文字列、NSData、またはその他の何かに変換する方法はないようです(これが私の目標ですが、 base64公開鍵を取得するだけです)。

しかし、modulusexponentを指定できる関数があり、それは公開鍵を計算するだけです。上記のSecKeyRefからログに記録されたデータを渡してテストしました。

しかし、どういうわけか、SecKeyRefオブジェクトからこれらのプロパティにアクセスできません(コンソールではオブジェクト全体しか表示できません。たとえば、_SecKeyRef.modulus_やその他の並べ替えはできません) 、それはようです)

私の質問:どうすれば_SecKeyRef.modulus_にアクセスできますか、またはこのSecKeyRefNSDataまたは類似のものに変換できますか?ありがとう

編集する

(詳細については)

私は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に変換します。

編集2

(2015年1月7日現在の既存の回答に関するコメント)

これは動作しません:

_let mirror = Mirror(reflecting: mySecKeyObject)

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

これにより、コンソールに次の出力が表示されます。

_Some <Raw SecKeyRef object>
_

文字列「Some」の意味がわかりません。

さらに、mirror.descendant("exponent")(または "modulus")の結果はnilになりますが、コンソールで未加工のオブジェクトを印刷すると、これらのプロパティが存在し、実際に入力されていることがわかります。

また、可能であれば、キーチェーンに保存し、NSDataとして読み取り、キーチェーンから削除する必要がないようにしたいと思います。バウンティの説明で述べたように、これが唯一の方法である場合は、信頼できるリファレンスを引用してください。これまでに提供されたすべての回答に感謝します。

25
Josh Beam

実際、どちらのキーチェーンnorプライベートAPIも使用せずに係数と指数を抽出することは可能です。

SecKeyCopyAttributesからCFDictionaryを抽出する( public but undocumented )関数SecKeyがあります。属性キーの有用なソースは SecItemConstants.c です。

この辞書の内容を調べると、エントリ"v_Data" : <binary>が見つかります。その内容は DER-encoded ASN for

SEQUENCE {
    modulus           INTEGER, 
    publicExponent    INTEGER
}

整数が正で、先頭に1ビットがある場合(2の補数の負の数値と混同しないように)、整数にはゼロバイトが埋め込まれるので、期待よりも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パーサーを作成したので、このコードはテストされていません。注意してください!)


privateキーの詳細を非常に同じ方法で抽出できることに注意してください。 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
}

ビット文字列は、上記の形式のDERエンコードされたASNです。

7
Raphael

Update以下の回答では、非公開APIの使用が原因でアプリが拒否される可能性があります。

答えは、AppleのオープンソースWebサイトの SecRSAKey.h ファイルにあります(セキュリティは、コードの一部であり、Apple opensourced)。ファイルは大きくありません。次の2つの重要な関数を宣言するもの:

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

これらの関数をブリッジヘッダーに追加して、Swiftからそれらを呼び出すことができます。また、これを行うときに、2つのタイプのフリーダイヤルブリッジとして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 )、2つの関数がすべてに存在するように見えます。さらに、SecurityはOSの重要なコンポーネントであるため、Appleが大幅に変更することはほとんどありません。

IOS 8.1、iOS 9.2、OSX 10.10.5でテストされ、コードは3つのプラットフォームすべてで動作します。

7
Cristik

SSL公開キーのピン留めを試みるのと同じ道を進んでいます。 APIはほとんど存在しません。私が見つけた解決策は、それをキーチェーンに入れて、それをNSDataとして取得できるようにすることでした(その後、Base64でエンコードできます)。それは恐ろしいことですが、1日程度の調査の後に(OpenSSLをアプリにバンドルすることに頼らずに)見つけることができる唯一のものは、.

私は自分のコードの一部をSwiftに移植しましたが、あまりテストしていないため、機能するかどうかは100%わかりません: https://Gist.github.com/chedabob/64a4cdc4a1194d815814 =

これは、このObj-Cコードに基づいています(これは、製品版アプリのように動作すると確信しています): https://Gist.github.com/chedabob/49eed109a3dfcad4bd41

6
chedabob

SecKeyのデータを取得する方法を見つけました。

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

これはうまく機能しているようで、公開鍵を正常に比較することができました。

これはSwift 3(Xcode 8 beta 3)にあります

3
jamone

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
*/
3
Yedy

放棄されたプロジェクトで、ASN.1パーサーの単一のObj-c再実装が機能しているようです。問題は、Swiftに変換する方法がわからない大量のポインタートリックを使用していることです(その一部が可能かどうかさえわからないためです)。 Swift取り込んだ入力はNSDataだけなので、それを囲むフレンドリーなラッパー。

ネット上のすべてのものは、キーチェーントリックでストアとリトリーブを使用して、pub-keyデータに到達します TrustKit のような本当に人気のあるライブラリですら。 SecKeyRefに関するAppleのドキュメント で根本的な原因への参照を見つけました(私はそう思います):

キーチェーンに格納されているキーのSecKeyRefオブジェクトをSecKeychainItemRefに安全にキャストして、キーチェーンアイテムとして操作できます。一方、SecKeyRefがキーチェーンに格納されていない場合、オブジェクトをSecKeychainItemRefにキャストし、それをキーチェーンサービス関数に渡すと、エラーが返されます。

現時点ではSecCertificateCopyValuesはiOSで利用できないため、証明書データの解析またはキーチェーンアイテムのシャッフルの実行に制限されています。

3
RyanR

SecCertificateCopyData()の使用について考えましたか?結果のCFDataはフリーダイヤルでブリッジされていると思います。

APIの関連ドキュメントについては、 https://developer.Apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/ を参照してください。

2
Tobi Nary

から アンマネージド<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によるとミラーが機能しない]

1
Cœur

私はスタックオーバーフローで他の人の答えに基づいてこれを書いた。現在、私はそれをプロダクションで使用していますが、キーチェーンに書き込む必要のない別のソリューションを使用できてうれしいです。

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