Подтвердить что ты не робот

Могу ли я получить модуль или экспоненту из объекта SecKeyRef в Swift?

В Swift я создал объект SecKeyRef, вызвав SecTrustCopyPublicKey на некотором необработанном X509 данные сертификата. Вот как выглядит этот объект 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.

Изменить 2

(комментарии по существующим ответам от 7 января 2015 года)

Это не работает:

let mirror = Mirror(reflecting: mySecKeyObject)

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

В результате этот результат выводится на консоль:

Some <Raw SecKeyRef object>

Не уверен, что означает строка "Некоторые".

Кроме того, mirror.descendant("exponent") (или "модуль" ) приводит к nil, хотя при печати необработанного объекта в консоли я могу ясно видеть, что эти свойства существуют и что они фактически заполнены.

Кроме того, если возможно, , я бы хотел избежать сохранения в цепочке ключей, считая NSData, а затем удаляя из брелка. Как указано в описании награды, если это единственный возможный способ, просьба привести авторитетную ссылку. Спасибо за все предоставленные ответы.

4b9b3361

Ответ 1

Я пошел по тому же пути, который пытался сделать SSL Public Key Pinning. API практически не существует, и решение, которое я нашел, это поместить его в Keychain, который затем можно получить как NSData (который может быть закодирован Base64). Это ужасно, но единственное, что я смог найти после дня или около того исследования (не прибегая к связыванию OpenSSL с моим приложением).

Я портировал часть моего кода на Swift, но я его не тестировал, поэтому я не уверен на 100%, что он работает: https://gist.github.com/chedabob/64a4cdc4a1194d815814

Он основан на этом коде Obj-C (который, я уверен, работает как в производственном приложении): https://gist.github.com/chedabob/49eed109a3dfcad4bd41

Ответ 2

Ответ находится в файле SecRSAKey.h с веб-сайта Apple с открытым исходным кодом (безопасность является частью кода, который Apple раскрыл). Файл невелик, и среди прочего он объявляет следующие две важные функции:

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

Вы можете добавить эти функции в свой заголовок моста, чтобы иметь возможность называть их из Swift, также при этом вы можете переключиться с CFDataRef на NSData*, поскольку два типа: бесплатный мост:

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 является критическим компонентом ОС, вряд ли Apple внесет существенные изменения в него.

Протестировано на iOS 8.1, iOS 9.2 и OSX 10.10.5, а код работает на всех трех платформах.

Ответ 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

Я обнаружил одну повторную реализацию Obj-c анализатора ASN.1 в заброшенном проекте, который, похоже, работает. Проблема в том, что она использует множество уловок, которые я не знаю, как перевести на Swift (даже не уверен, что некоторые из них возможны). Должно быть возможно создать быструю дружественную оболочку вокруг него, поскольку единственным входным данным, который он принимает, является NSData.

Все в сети использует хранилище и извлекает из трюка Keychain, чтобы получить данные паб-ключа, даже действительно популярные библиотеки, такие как TrustKit. Я нашел ссылку в Apple docs на SecKeyRef на основную причину (я думаю):

Объект SecKeyRef для ключа, который хранится в цепочке ключей, может быть безопасно отбрасывается в SecKeychainItemRef для манипулирования в качестве брелка пункт. С другой стороны, если SecKeyRef не хранится в цепочке ключей, бросая объект в SecKeychainItemRef и передавая его в Keychain Функции служб возвращают ошибки.

Так как SecCertificateCopyValues в настоящее время недоступен в iOS, вы ограничены либо синтаксическим анализом данных сертификата, либо перетасовкой элемента Keychain Item.

Ответ 6

Действительно, можно извлечь модуль и показатель, не используя ни keychains, ни private API.

Существует (общедоступный, но недокументированный) function SecKeyCopyAttributes, который извлекает CFDictionary из SecKey. Полезным источником ключей атрибутов является SecItemConstants.c

Проверяя содержимое этого словаря, мы найдем запись "v_Data" : <binary>. Его содержание DER-encoded ASN для

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
}

Бит-строка представляет собой ASN в форме DER формы выше.

Ответ 7

Из Как кодировать неуправляемый <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
}

Но если вы хотите избежать добавления в цепочку ключей, вы можете использовать Mirror:

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

[edit: Зеркало не работает в соответствии с Джошем]

Ответ 8

Я нашел, как получить данные для SecKey.

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

Это, кажется, работает хорошо, и я смог успешно сравнить открытые ключи.

Это в Swift 3 (Xcode 8 beta 3)

Ответ 9

Я написал эту базу на другом ответе в 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;
}