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公開鍵を取得するだけです)。
しかし、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>
_
文字列「Some」の意味がわかりません。
さらに、mirror.descendant("exponent")
(または "modulus")の結果はnil
になりますが、コンソールで未加工のオブジェクトを印刷すると、これらのプロパティが存在し、実際に入力されていることがわかります。
また、可能であれば、キーチェーンに保存し、NSData
として読み取り、キーチェーンから削除する必要がないようにしたいと思います。バウンティの説明で述べたように、これが唯一の方法である場合は、信頼できるリファレンスを引用してください。これまでに提供されたすべての回答に感謝します。
実際、どちらのキーチェーン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です。
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つのプラットフォームすべてで動作します。
SSL公開キーのピン留めを試みるのと同じ道を進んでいます。 APIはほとんど存在しません。私が見つけた解決策は、それをキーチェーンに入れて、それをNSDataとして取得できるようにすることでした(その後、Base64でエンコードできます)。それは恐ろしいことですが、1日程度の調査の後に(OpenSSLをアプリにバンドルすることに頼らずに)見つけることができる唯一のものは、.
私は自分のコードの一部をSwiftに移植しましたが、あまりテストしていないため、機能するかどうかは100%わかりません: https://Gist.github.com/chedabob/64a4cdc4a1194d815814 =
これは、このObj-Cコードに基づいています(これは、製品版アプリのように動作すると確信しています): https://Gist.github.com/chedabob/49eed109a3dfcad4bd41
SecKey
のデータを取得する方法を見つけました。
let publicKey: SecKey = ...
let data = SecKeyCopyExternalRepresentation(publicKey, nil)
これはうまく機能しているようで、公開鍵を正常に比較することができました。
これはSwift 3(Xcode 8 beta 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
*/
放棄されたプロジェクトで、ASN.1パーサーの単一のObj-c再実装が機能しているようです。問題は、Swiftに変換する方法がわからない大量のポインタートリックを使用していることです(その一部が可能かどうかさえわからないためです)。 Swift取り込んだ入力はNSDataだけなので、それを囲むフレンドリーなラッパー。
ネット上のすべてのものは、キーチェーントリックでストアとリトリーブを使用して、pub-keyデータに到達します TrustKit のような本当に人気のあるライブラリですら。 SecKeyRefに関するAppleのドキュメント で根本的な原因への参照を見つけました(私はそう思います):
キーチェーンに格納されているキーのSecKeyRefオブジェクトをSecKeychainItemRefに安全にキャストして、キーチェーンアイテムとして操作できます。一方、SecKeyRefがキーチェーンに格納されていない場合、オブジェクトをSecKeychainItemRefにキャストし、それをキーチェーンサービス関数に渡すと、エラーが返されます。
現時点ではSecCertificateCopyValues
はiOSで利用できないため、証明書データの解析またはキーチェーンアイテムのシャッフルの実行に制限されています。
SecCertificateCopyData()
の使用について考えましたか?結果のCFData
はフリーダイヤルでブリッジされていると思います。
APIの関連ドキュメントについては、 https://developer.Apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/ を参照してください。
から アンマネージド<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によるとミラーが機能しない]
私はスタックオーバーフローで他の人の答えに基づいてこれを書いた。現在、私はそれをプロダクションで使用していますが、キーチェーンに書き込む必要のない別のソリューションを使用できてうれしいです。
- (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;
}