2010-05-19 2 views
4

Я пытаюсь подписать некоторые данные с помощью SecKeyRawSign, но я продолжаю получать -4 errSecUnimplemented. Это кажется странным, поскольку в документации указано, что он доступен в iPhone OS2.0 и более поздних версиях.Использование SecKeyRawSign на iPhone

Кто-нибудь мог использовать эту функцию? Если да, есть ли какие-либо трюки?

~ Nate

ответ

1

-4 errSecUnimplemented ошибка была вызвана плохой ссылкой на частный ключ, используемый для подписи данных. Ошибка в этой ситуации. A errSecParam было бы лучше.

~ Nate

+0

Это была именно та проблема, с которой я столкнулся. Оказывается, класс SecKeyWrapper в CryptoExercise не сохранил закрытый ключ в цепочке ключей! – mikeho

4

Если вы с этой проблемой, скорее всего, это происходит потому, что закрытый ключ вы сгенерировали не фактически сохраняются в связке ключей. Я понял это, когда останавливал и перезапускал приложение, а подпись сообщения не работала.

Итак, вот мои методы, чтобы сделать эту работу.

Это один генерирует пару ключей,

- (void)generateKeyPair:(NSUInteger)keySize { 
    OSStatus sanityCheck = noErr; 
    publicKeyRef = NULL; 
    privateKeyRef = NULL; 

    LOGGING_FACILITY1(keySize == 512 || keySize == 1024 || keySize == 2048, @"%d is an invalid and unsupported key size.", keySize); 

    // First delete current keys. 
    [self deleteAsymmetricKeys]; 

    // Container dictionaries. 

    // See SecKey.h for other values 
    NSDictionary *privateKeyDict = @{ 
        (__bridge id) kSecAttrIsPermanent : [NSNumber numberWithBool:YES], 
        (__bridge id) kSecAttrApplicationTag : privateTag 
    }; 

    // See SecKey.h for other values 
    NSDictionary *publicKeyDict = @{ 
        (__bridge id) kSecAttrIsPermanent : [NSNumber numberWithBool:YES], 
        (__bridge id) kSecAttrApplicationTag : publicTag 
    }; 

    NSDictionary *keyPairDict = @{ 
        (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA, 
        (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize], 
        (__bridge id) kSecPrivateKeyAttrs : privateKeyDict, 
        (__bridge id) kSecPublicKeyAttrs : publicKeyDict 
    }; 

    // SecKeyGeneratePair returns the SecKeyRefs 
    sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef) keyPairDict, &publicKeyRef, &privateKeyRef); 
    LOGGING_FACILITY(sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL, @"Something really bad went wrong with generating the key pair."); 

    // retrieve the actual bits for the keys, not just the references 
    NSData *publicKeyBits = [self getKeyBitsFromKey:publicKeyRef]; 
    NSData *privateKeyBits = [self getKeyBitsFromKey:privateKeyRef]; 

    // save the keys to the keychain 
    [self saveKeyToKeychain:publicKeyBits keySize:keySize private:NO]; 
    [self saveKeyToKeychain:privateKeyBits keySize:keySize private:YES]; 
} 

** EDIT **

IOS 9 представил новую функцию под названием Secure Анклав. Если вы хотите сгенерировать ключ, который будет там храниться и только там, вам потребуется использовать ключ 256-bit EC, так как это единственный тип, поддерживаемый анклавом. keyPairDict будет выглядеть следующим образом, вместо:

NSDictionary *keyPairDict = @{ 
       (__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave, 
       (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC, 
       // we can use keySize here if we want 
       // but since 256 is the only available size 
       // we can just hardcode it for now 
       (__bridge id) kSecAttrKeySizeInBits : @256], 
       (__bridge id) kSecPrivateKeyAttrs : privateKeyDict, 
       (__bridge id) kSecPublicKeyAttrs : publicKeyDict 
}; 

Я знаю, что параметры являются правильными, но я сам не проверял Secure Анклав еще, так что дайте мне знать, если это не работает по какой-то причине.

Кроме того, для справки: a 256-bit EC ключ является эквивалентом ключа 3072-bit RSA.

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

NSDictionary *queryKey = @{ 
       (__bridge id) kSecClass : (__bridge id) kSecClassKey, 
       (__bridge id) kSecAttrApplicationTag : tempTag, 
       (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC 
}; 

Поскольку Secure Enclave, хорошо, безопасно, вы, скорее всего, не сможете получить секретный ключ бит , Скорее всего, вы сможете создать ссылку. Но вам все равно не нужно обрабатывать данные секретного ключа.

** END EDIT **

Этот метод извлекает фактические биты от брелка, а не просто ссылка

- (NSData *)getKeyBitsFromKey:(SecKeyRef)givenKey { 
    static const uint8_t publicKeyIdentifier[] = "com.sample.temp"; 
    NSData *tempTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)]; 

    NSDictionary *queryKey = @{ 
        (__bridge id) kSecClass : (__bridge id) kSecClassKey, 
        (__bridge id) kSecAttrApplicationTag : tempTag, 
        (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA 
    }; 

    // Temporarily add key to the Keychain, return as data: 
    NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithDictionary:queryKey copyItems:YES]; 
    [attributes setObject:(__bridge id) givenKey forKey:(__bridge id) kSecValueRef]; 
    [attributes setObject:@YES forKey:(__bridge id) kSecReturnData]; 

    // result codes: https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html#//apple_ref/doc/uid/TP30000157-CH4g-339030 
    OSStatus sanityCheck = noErr; 
    NSData *keyBits = nil; 

    CFTypeRef result; 
    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result); 
    if (sanityCheck == errSecSuccess) { 
      keyBits = CFBridgingRelease(result); 

      // Remove from Keychain again: 
      (void) SecItemDelete((__bridge CFDictionaryRef) queryKey); 
      return keyBits; 
    } 
    else if (sanityCheck == errSecDuplicateItem) { 
      // Remove from Keychain again: 
      (void) SecItemDelete((__bridge CFDictionaryRef) queryKey); 
      return [self getKeyBitsFromKey:givenKey]; 
    } 

    return nil; 
} 

Этот метод сохраняет биты брелка

- (void)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate { 
    OSStatus sanityCheck = noErr; 
    NSData *tag; 
    id keyClass; 
    if (isPrivate) { 
      tag = privateTag; 
      keyClass = (__bridge id) kSecAttrKeyClassPrivate; 
    } 
    else { 
      tag = publicTag; 
      keyClass = (__bridge id) kSecAttrKeyClassPublic; 
    } 

    NSDictionary *saveDict = @{ 
        (__bridge id) kSecClass : (__bridge id) kSecClassKey, 
        (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA, 
        (__bridge id) kSecAttrApplicationTag : tag, 
        (__bridge id) kSecAttrKeyClass : keyClass, 
        (__bridge id) kSecValueData : key, 
        (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize], 
        (__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize], 
        (__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse, 
        (__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue, 
        (__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse, 
        (__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue, 
        (__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse, 
        (__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue, 
        (__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse 
    }; 

    SecKeyRef savedKey = NULL; 
    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey); 
    if (sanityCheck != errSecSuccess) { 
      LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck); 
    } 
} 

И тогда вы подписываете так:

- (NSData *)getSignatureBytes:(NSData *)plainText { 
    OSStatus sanityCheck = noErr; 
    NSData *signedHash = nil; 

    uint8_t *signedHashBytes = NULL; 
    size_t signedHashBytesSize = 0; 

    SecKeyRef privateKey = NULL; 

    privateKey = [self getKeyRef:YES]; 
    signedHashBytesSize = SecKeyGetBlockSize(privateKey); 

    // Malloc a buffer to hold signature. 
    signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t)); 
    memset((void *) signedHashBytes, 0x0, signedHashBytesSize); 

    // Sign the SHA1 hash. 
    sanityCheck = SecKeyRawSign(privateKey, 
      kTypeOfSigPadding, 
      (const uint8_t *) [[self getHashBytes:plainText] bytes], 
      kChosenDigestLength, 
      signedHashBytes, 
      &signedHashBytesSize 
    ); 

    LOGGING_FACILITY1(sanityCheck == noErr, @"Problem signing the SHA1 hash, OSStatus == %d.", sanityCheck); 

    // Build up signed SHA1 blob. 
    signedHash = [NSData dataWithBytes:(const void *) signedHashBytes length:(NSUInteger) signedHashBytesSize]; 

    if (signedHashBytes) { 
     free(signedHashBytes); 
    } 

    return signedHash; 
} 
+0

iOS 9 позволяет хранить секретный ключ в защищенном анклаве, какие изменения, если они необходимы, чтобы воспользоваться этим? (очевидно, что приватный ключ не может быть экспортирован, но было бы хорошо убедиться в этом) – LamonteCristo

+0

@LamonteCristo, потому что поле комментариев более ограничено, я добавил приведенное выше изменение, чтобы ответить на ваш вопрос. – mikeho

+1

@LamonteCristo Я не касался этого кода в то время, поэтому я пересматриваю его, когда получаю шанс. – mikeho

Смежные вопросы