2010-05-19 10 views
4

J'essaye de signer des données en utilisant SecKeyRawSign mais je continue à obtenir un -4 errSecUnimplemented. Cela semble étrange puisque la documentation indique qu'il est disponible dans iPhone OS2.0 et plus tard.Utilisation de SecKeyRawSign sur l'iPhone

Est-ce que quelqu'un a été capable d'utiliser cette fonction? Si oui, y a-t-il des astuces?

~ Nate

Répondre

1

La -4 errSecUnimplemented erreur a été causée par une mauvaise référence à la clé privée utilisée pour signer les données. Erreur de confusion pour cette situation. Un errSecParam aurait été plus agréable.

~ NATE

+0

C'était exactement le problème que j'avais. Il s'avère que la classe SecKeyWrapper du CryptoExercise n'enregistrait pas la clé privée dans le trousseau! – mikeho

4

Si vous rencontrez ce problème, plus il est probable que la clé privée que vous avez généré n'est pas réellement enregistré dans le trousseau. J'ai compris cela quand arrêter et redémarrer l'application et signer le message ne fonctionnait pas.

Voici donc mes méthodes pour faire ce travail.

Celui-ci génère la paire de clés

- (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 introduit une nouvelle fonctionnalité appelée le sécurisé Enclave. Si vous voulez générer une clé qui sera stockée là et seulement là, vous devrez utiliser une clé 256-bit EC, car c'est le seul type supporté par l'enclave. Le keyPairDict ressemblera à ceci à la place:

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

Je sais que les paramètres sont corrects, mais je ne me suis pas encore testé l'Enclave sécurisé, alors laissez-moi savoir si cela ne fonctionne pas pour une raison quelconque.

Par ailleurs, pour référence: une clé 256-bit EC est équivalente à une clé 3072-bit RSA.

La requête utilisée pour récupérer la clé ci-dessous serait également différente:

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

Parce que l'Enclave est sécurisé, bien sûr, vous avez très probablement ne sera pas en mesure de récupérer les bits de clé privée . Très probablement, vous ne pourrez générer qu'une référence. Mais vous ne devriez pas avoir à manipuler les données de clé privée de toute façon.

** FIN EDIT **

Cette méthode récupère les bits réels du trousseau et non seulement la référence

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

Cette méthode permet d'économiser les bits au trousseau

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

Ensuite, vous signez comme suit:

- (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 permet de stocker la clé privée dans l'enclave sécurisée, quels sont les changements nécessaires pour en profiter? (évidemment la clé privée ne peut pas être exportée, mais il serait bon de vérifier cela) – LamonteCristo

+0

@LamonteCristo parce que la boîte de commentaire est plus limitée, j'ai ajouté une modification ci-dessus pour répondre à votre question. – mikeho

+1

@LamonteCristo Je n'ai pas touché à ce code depuis un moment, je vais donc le revoir quand j'en aurai l'occasion. – mikeho