Criptografia AES para um NSString no iPhone




objective-c encryption (3)

@owlstead, em relação ao seu pedido de "uma variante criptograficamente segura de uma das respostas dadas", consulte o RNCryptor . Ele foi projetado para fazer exatamente o que você está solicitando (e foi criado em resposta aos problemas com o código listado aqui).

O RNCryptor usa o PBKDF2 com sal, fornece um IV aleatório e conecta o HMAC (também gerado a partir do PBKDF2 com seu próprio sal. Ele suporta operação síncrona e assíncrona.

Alguém pode me apontar na direção certa para poder criptografar uma string, retornando outra string com os dados criptografados? (Eu tenho tentado com criptografia AES256.) Eu quero escrever um método que leve duas instâncias de NSString, sendo uma delas a mensagem para criptografar e a outra sendo uma 'senha' para criptografá-la - eu suspeito que teria que gerar a chave de criptografia com a senha, de uma maneira que pode ser revertida se a senha for fornecida com os dados criptografados. O método deve então retornar um NSString criado a partir dos dados criptografados.

Eu tentei a técnica detalhada no primeiro comentário neste post , mas não tive sorte até agora. O CryptoExercise da Apple certamente tem algo, mas eu não consigo entender ... Eu vi muitas referências ao CCCrypt , mas falhou em todos os casos em que o usei.

Eu também teria que ser capaz de descriptografar uma string criptografada, mas espero que seja tão simples quanto o kCCEncrypt / kCCDecrypt.


Eu esperei um pouco no @QuinnTaylor para atualizar sua resposta, mas como ele não o fez, aqui está a resposta um pouco mais clara e de uma maneira que será carregada no XCode7 (e talvez maior). Eu usei isso em um aplicativo Cocoa, mas provavelmente funcionará bem com um aplicativo iOS também. Não tem erros de ARC.

Cole antes de qualquer seção @implementation no seu arquivo 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

Cole essas duas funções na classe @implementation desejada. No meu caso, escolhi @implementation AppDelegate no meu arquivo 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];
}

Como você não postou nenhum código, é difícil saber exatamente quais problemas você está encontrando. No entanto, a postagem do blog que você vincula parece funcionar muito bem ... além da vírgula extra em cada chamada para CCCrypt() que causou erros de compilação.

Um comentário posterior sobre esse post inclui este código adaptado , que funciona para mim e parece um pouco mais simples. Se você incluir seu código para a categoria NSData, você pode escrever algo assim: (Nota: As chamadas printf() são apenas para demonstrar o estado dos dados em vários pontos - em um aplicativo real, não faria sentido imprima esses valores.)

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

Dado este código, e o fato de que os dados criptografados nem sempre serão bem traduzidos em um NSString, pode ser mais conveniente escrever dois métodos que envolvam a funcionalidade que você precisa, em avanço e reversão ...

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

Isso definitivamente funciona no Snow Leopard, e o @Boz relata que o CommonCrypto faz parte do Core OS no iPhone. Ambos 10.4 e 10.5 têm /usr/include/CommonCrypto , embora 10.5 tenha uma página man para CCCryptor.3cc e 10.4 não, então YMMV.

EDIT: Veja esta pergunta de acompanhamento sobre o uso de codificação Base64 para representar bytes de dados criptografados como uma seqüência de caracteres (se desejado) usando conversões seguras, sem perdas.





aes