web-dev-qa-db-fra.com

Cryptage AES pour NSString sur l'iPhone

Quelqu'un peut-il m'indiquer la bonne direction pour pouvoir chiffrer une chaîne en renvoyant une autre chaîne avec les données chiffrées? (J'ai essayé avec le chiffrement AES256.) Je souhaite écrire une méthode prenant deux instances de NSString, l'une étant le message à chiffrer et l'autre, un "code d'accès" permettant de le chiffrer - je suppose que je devrais générer la clé de cryptage avec le code d'accès, de manière à pouvoir être inversée si le code d'accès est fourni avec les données cryptées. La méthode devrait alors renvoyer une chaîne NSString créée à partir des données chiffrées.

J'ai essayé la technique détaillée dans le premier commentaire sur ce post , mais je n'ai pas eu de chance jusqu'à présent. CryptoExercise d'Apple a certainement quelque chose, mais je ne peux pas en comprendre le sens ... J'ai vu beaucoup de références à CCCrypt , mais c'est un échec dans tous les cas. Je l'ai utilisé.

Je devrais également être capable de déchiffrer une chaîne chiffrée, mais j'espère que c'est aussi simple que kCCEncrypt/kCCDecrypt.

123
Boz

Puisque vous n'avez posté aucun code, il est difficile de savoir exactement quels problèmes vous rencontrez. Cependant, la publication de blog vers laquelle vous créez un lien semble fonctionner assez correctement ... mis à part la virgule supplémentaire dans chaque appel à CCCrypt() qui a provoqué des erreurs de compilation.

Un commentaire ultérieur sur ce post inclut ce code adapté , qui fonctionne pour moi et semble un peu plus simple. Si vous incluez leur code pour la catégorie NSData, vous pouvez écrire quelque chose comme ceci: (Remarque: les appels printf() ne servent qu'à montrer l'état des données à différents points - dans une application réelle, cela ne servirait à rien. pas logique d’imprimer de telles valeurs.)

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString *key = @"my password";
    NSString *secret = @"text to encrypt";

    NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding];
    NSData *cipher = [plain AES256EncryptWithKey:key];
    printf("%s\n", [[cipher description] UTF8String]);

    plain = [cipher AES256DecryptWithKey:key];
    printf("%s\n", [[plain description] UTF8String]);
    printf("%s\n", [[[NSString alloc] initWithData:plain encoding:NSUTF8StringEncoding] UTF8String]);

    [pool drain];
    return 0;
}

Étant donné ce code et le fait que les données chiffrées ne se traduisent pas toujours dans une chaîne NSString, il peut être plus pratique d'écrire deux méthodes qui englobent la fonctionnalité dont vous avez besoin, en avant et en arrière ...

- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
    return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}

- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
    return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
                                  encoding:NSUTF8StringEncoding] autorelease];
}

Cela fonctionne vraiment sur Snow Leopard, et @ Boz indique que CommonCrypto fait partie du système d'exploitation Core de l'iPhone. 10.4 et 10.5 ont /usr/include/CommonCrypto, bien que 10.5 ait une page de manuel pour CCCryptor.3cc et 10.4 non, donc YMMV.


EDIT: Voir cette question suivante sur l'utilisation du codage Base64 pour représenter les octets de données cryptés sous forme de chaîne (si nécessaire) à l'aide de conversions sûres et sans perte.

127
Quinn Taylor

J'ai rassemblé une collection de catégories pour NSData et NSString qui utilisent les solutions trouvées sur blog de Jeff LaMarche et quelques conseils de Quinn Taylor ici sur Stack Overflow.

Il utilise des catégories pour étendre NSData afin de fournir le cryptage AES256 et offre également une extension de NSString à l'encodage de données cryptées en mode BASE64 en toute sécurité.

Voici un exemple pour montrer l'utilisation du chiffrement de chaînes:

NSString *plainString = @"This string will be encrypted";
NSString *key = @"YourEncryptionKey"; // should be provided by a user

NSLog( @"Original String: %@", plainString );

NSString *encryptedString = [plainString AES256EncryptWithKey:key];
NSLog( @"Encrypted String: %@", encryptedString );

NSLog( @"Decrypted String: %@", [encryptedString AES256DecryptWithKey:key] );

Obtenez le code source complet ici:

https://Gist.github.com/838614

Merci pour tous les conseils utiles!

-- Michael

45
Michael Thiel

@owlstead, en ce qui concerne votre demande concernant "une variante sécurisée du point de vue cryptographique de l'une des réponses données", veuillez consulter RNCryptor . Il a été conçu pour faire exactement ce que vous demandez (et a été conçu en réponse aux problèmes rencontrés avec le code répertorié ici).

RNCryptor utilise PBKDF2 avec salt, fournit un IV aléatoire et associe HMAC (également généré à partir de PBKDF2 avec son propre sel. Il prend en charge les opérations synchrones et asynchrones.

34
Rob Napier

J'ai attendu un peu sur @QuinnTaylor pour mettre à jour sa réponse, mais comme il ne l'a pas fait, voici la réponse un peu plus clairement et d'une manière qui se chargera sur XCode7 (et peut-être plus). Je l'ai utilisé dans une application Cocoa, mais cela fonctionnera probablement aussi avec une application iOS. N'a pas d'erreurs ARC.

Coller avant toute section @implementation dans votre fichier AppDelegate.m ou AppDelegate.mm.

#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (AES256)

- (NSData *)AES256EncryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES256,
                                     NULL /* initialization vector (optional) */,
                                     [self bytes], dataLength, /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

- (NSData *)AES256DecryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES256,
                                     NULL /* initialization vector (optional) */,
                                     [self bytes], dataLength, /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesDecrypted);

    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

@end

Collez ces deux fonctions dans la classe @implementation de votre choix. Dans mon cas, j'ai choisi @implementation AppDelegate dans mon fichier AppDelegate.mm ou AppDelegate.m.

- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
    NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
    return [data base64EncodedStringWithOptions:kNilOptions];
}

- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
    NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions];
    return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding];
}
10
Volomike