125 votes

Cryptage AES d'une chaîne NSString sur l'iPhone

Quelqu'un peut-il m'indiquer la voie à suivre pour crypter une chaîne de caractères et renvoyer une autre chaîne contenant les données cryptées ? (Je veux écrire une méthode qui prend deux instances NSString, l'une étant le message à crypter et l'autre étant un "code de passe" pour le crypter - je pense que je devrais générer la clé de cryptage avec le code de passe, d'une manière qui peut être inversée si le code de passe est fourni avec les données cryptées. La méthode devrait ensuite retourner une NSString créée à partir des données cryptées.

J'ai essayé la technique détaillée dans le premier commentaire sur cet article mais je n'ai pas eu de chance jusqu'à présent. Le site d'Apple CryptoExercice a certainement quelque chose, mais je n'arrive pas à lui donner un sens... J'ai vu beaucoup de références à CCCrypt mais il a échoué dans tous les cas où je l'ai utilisé.

Il faudrait aussi que je puisse décrypter une chaîne cryptée, mais j'espère que c'est aussi simple que kCCEncrypt/kCCDecrypt.

1 votes

Veuillez noter que j'ai donné une prime pour une réponse de Rob Napier qui a fourni une réponse à la question suivante : " Comment puis-je avoir une réponse ? sécurisé version de la réponse.

128voto

Quinn Taylor Points 29688

Comme vous n'avez pas posté de code, il est difficile de savoir exactement quels sont les problèmes que vous rencontrez. Cependant, l'article du blog dont vous donnez le lien semble fonctionner de manière assez décente... à part la virgule supplémentaire dans chaque appel à CCCrypt() ce qui provoquait des erreurs de compilation.

Un commentaire ultérieur sur ce poste comprend 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 : (Note : Le code de la catégorie printf() ne servent qu'à démontrer l'état des données à divers points - dans une application réelle, il ne serait pas judicieux 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;
}

Compte tenu de ce code, et du fait que les données cryptées ne se traduiront pas toujours joliment dans un NSString, il peut être plus pratique d'écrire deux méthodes qui enveloppent 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 définitivement sur Snow Leopard, et @Boz rapporte que CommonCrypto fait partie du Core OS sur l'iPhone. Les versions 10.4 et 10.5 ont /usr/include/CommonCrypto bien que 10.5 ait une page de manuel pour CCCryptor.3cc et 10.4 ne le fait pas, donc YMMV.


EDIT : Voir cette question de suivi sur l'utilisation de l'encodage Base64 pour représenter les octets de données cryptées sous forme de chaîne (si souhaité) en utilisant des conversions sûres et sans perte.

1 votes

Merci. CommonCrypto fait partie du système d'exploitation principal de l'iPhone, et j'utilise également la version 10.6.

1 votes

J'ai fait -1, car le code référencé est dangereusement peu sûr. Regardez plutôt la réponse de Rob Napier. Son article de blog " robnapier.net/aes-commoncrypto détaille exactement pourquoi c'est insécurisant.

1 votes

Cette solution ne fonctionne pas dans mon cas. J'ai une chaîne de caractères que je veux décoder : U2FsdGVkX1+MEhsbofUNj58m+8tu9ifAKRiY/Zf8YIw= et j'ai la clé : 3841b8485cd155d932a2d601b8cee2ec . Je n'arrive pas à décrypter la chaîne en utilisant la clé avec votre solution. Merci

46voto

Michael Thiel Points 1279

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

Il utilise des catégories pour étendre NSData afin de fournir un cryptage AES256 et offre également une extension de NSString pour coder en toute sécurité les données cryptées en chaînes BASE64.

Voici un exemple pour montrer l'utilisation pour le cryptage des chaînes de caractères :

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 ces conseils utiles !

-- Michael

0 votes

NSString *key = @ "YourEncryptionKey" ; // doit être fournie par un utilisateur Peut-on générer une clé aléatoire sécurisée de 256 bits, au lieu de celle fournie par l'utilisateur.

0 votes

Le lien vers Jeff LaMarche est cassé

0 votes

@michael - pouvez-vous s'il vous plaît me guider dans ceci stackoverflow.com/questions/63632975/ Merci

35voto

Rob Napier Points 92148

@owlstead, concernant votre demande de "variante cryptographiquement sécurisée d'une des réponses données", veuillez voir RNCryptor . Il a été conçu pour faire exactement ce que vous demandez (et a été construit en réponse aux problèmes avec le code listé ici).

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

0 votes

Un code intéressant, qui vaut probablement les points. Quel est le nombre d'itérations pour le PBKDF2 et sur quoi calculez-vous le HMAC ? Je présume que ce sont les données cryptées ? Je n'ai pas pu trouver cela facilement dans la documentation fournie.

0 votes

Consultez la rubrique "Meilleures pratiques en matière de sécurité" pour plus de détails. Je recommande 10k itérations sur iOS (~80ms sur un iPhone 4). Et oui, crypter plus que HMAC. Je regarderai probablement la page "Data format" ce soir pour m'assurer qu'elle est à jour pour la v2.0 (la documentation principale est à jour, mais je ne me souviens pas si j'ai révisé la page sur le format des données).

0 votes

Ah, oui, j'ai trouvé le nombre de tours dans la documentation et j'ai regardé le code. Je vois des fonctions de nettoyage et des clés HMAC et de cryptage séparées. Si le temps le permet, j'essaierai d'y regarder de plus près demain. Puis j'attribuerai les points.

13voto

Volomike Points 7083

J'ai attendu un peu que @QuinnTaylor mette à jour sa réponse, mais comme il ne l'a pas fait, voici la réponse un peu plus clairement et de manière à ce qu'elle se charge sur XCode7 (et peut-être plus). Je l'ai utilisé dans une application Cocoa, mais il est probable que cela fonctionne bien avec une application iOS également. Il n'y a pas d'erreurs ARC.

Collez-la 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 que vous souhaitez. 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];
}

0voto

SURESH SANKE Points 393
Please use the below mentioned URL to encrypt string using AES excryption with 
key and IV values.

https://github.com/muneebahmad/AESiOSObjC

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X