İPhone'da NSString için AES Şifreleme


124

Herhangi biri beni bir dizeyi şifreleyip şifrelenmiş verilerle başka bir dizge döndürmek için doğru yönü gösterebilir mi? (AES256 şifrelemesini deniyordum.) İki NSString örneğini alan bir yöntem yazmak istiyorum, biri şifrelenecek mesaj, diğeri şifrelemek için 'parola' - oluşturmam gerekeceğinden şüpheleniyorum şifreleme anahtarı, şifrelenmiş verilerle birlikte verilirse tersine çevrilebilecek şekilde şifreleme anahtarı ile şifreleme anahtarı. Yöntem daha sonra şifrelenmiş verilerden oluşturulan bir NSString döndürmelidir.

Bu yazının ilk yorumunda detaylandırılan tekniği denedim ama şimdiye kadar hiç şansım olmadı. Apple'ın CryptoExercise'ında kesinlikle bir şey var, ama bunu bir anlam ifade edemiyorum ... CCCrypt'e birçok referans gördüm , ancak kullandığım her durumda başarısız oldu.

Ayrıca şifrelenmiş bir dizenin şifresini çözebilmem gerekirdi, ancak umarım bu kCCEncrypt / kCCDecrypt kadar basittir.


1
Cevabın güvenli bir versiyonunu sağlayan Rob Napier'in bir cevabına ödül verdiğimi lütfen unutmayın .
Maarten Bodewes

Yanıtlar:


126

Herhangi bir kod göndermediğiniz için, tam olarak hangi problemlerle karşılaştığınızı bilmek zordur. Bununla birlikte, bağlantı kurduğunuz blog gönderisi, CCCrypt()derleme hatalarına neden olan her çağrıdaki ekstra virgül dışında oldukça düzgün çalışıyor gibi görünüyor .

Bu gönderi hakkında daha sonraki bir yorum, benim için çalışan ve biraz daha basit görünen bu uyarlanmış kodu içerir . NSData kategorisi için kodlarını dahil ederseniz, şöyle bir şey yazabilirsiniz: (Not: printf()Çağrılar yalnızca çeşitli noktalardaki verilerin durumunu göstermek içindir - gerçek bir uygulamada, bu tür değerleri yazdırmak mantıklı olmaz .)

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

Bu kod göz önüne alındığında ve şifrelenmiş verilerin her zaman bir NSString'e güzel bir şekilde çevrilmeyeceği gerçeği göz önüne alındığında, ihtiyacınız olan işlevselliği ileri ve geri saran iki yöntem yazmak daha uygun olabilir ...

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

Bu kesinlikle Snow Leopard üzerinde çalışıyor ve @Boz , CommonCrypto'nun iPhone'daki Core OS'nin bir parçası olduğunu bildiriyor. 10.4 ve 10.5'in her ikisi de vardır /usr/include/CommonCrypto, ancak 10.5'in man sayfası vardır CCCryptor.3ccve 10.4'te yoktur, yani YMMV.


DÜZENLEME: Güvenli, kayıpsız dönüştürmeler kullanarak şifrelenmiş veri baytlarını bir dize (istenirse) olarak temsil etmek için Base64 kodlamasını kullanma hakkındaki bu takip sorusuna bakın .


1
Teşekkürler. CommonCrypto, iPhone'daki Core OS'nin bir parçası ve ben de 10.6 çalıştırıyorum.
Boz

1
-1 yaptım çünkü başvurulan kod tehlikeli derecede güvenli değil. Bunun yerine Rob Napier'in cevabına bakın. Blog yazısı " robnapier.net/aes-commoncrypto , bunun neden güvensiz olduğunu tam olarak ayrıntılarıyla açıklıyor.
Erik Engheim

1
Bu çözüm benim durumumda çalışmıyor. Kodunu çözmek istediğim bir dizem var: U2FsdGVkX1 + MEhsbofUNj58m + 8tu9ifAKRiY / Zf8YIw = ve şu anahtara sahibim: 3841b8485cd155d932a2d601b8cee2ec. Çözümünüzle birlikte anahtarı kullanarak dizenin şifresini çözemiyorum. Teşekkürler
George

Bu çözüm, XCode7 ile El Capitan'da bir Cocoa uygulamasında çalışmaz. ARC autorelease,.
Volomike

@QuinnTaylor Bu yanıtı düzenleyebilirim, ancak size uygun gördüğünüz şekilde değiştirme fırsatı vermek istedim. Kodunuzu burada onardım . Ayrıca, uyarlanmış kod olmadan derlenmeyeceğini belirtmek isteyebilirsiniz . Bu yüzden, XCode7 ile El Capitan'da bir Cocoa uygulaması üzerinde çalışmasını sağladım. Şimdi yapmaya çalıştığım şey, bu verileri Base64Encode / Base64Decode işleminin nasıl yapılacağını bulmaktır, böylece ham verileri döndürmek yerine, aktarım sırasında rahatsız edilmeden iletilebilir.
Volomike

46

NSData ve NSString için Jeff LaMarche'ın blogunda bulunan çözümleri ve Quinn Taylor tarafından Stack Overflow'da bazı ipuçlarını kullanan bir kategori koleksiyonu oluşturdum .

AES256 şifrelemesini sağlamak için NSData'yı genişletmek için kategoriler kullanır ve ayrıca NSString'in BASE64 şifreli verileri güvenli bir şekilde dizelere genişletmesine yönelik bir uzantı sunar.

Dizeleri şifrelemenin kullanımını gösteren bir örnek:

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] );

Tam kaynak kodunu buradan alın:

https://gist.github.com/838614

Tüm faydalı ipuçları için teşekkürler!

- Michael


NSString * key = @ "YourEncryptionKey"; // bir kullanıcı tarafından sağlanmalıdır Kullanıcı tarafından sağlanan yerine rastgele güvenli 256 bitlik bir anahtar oluşturabilir miyiz?
Pranav Jaiswal

Jeff LaMarche bağlantısı koptu
whyoz

35

@owlstead, "verilen yanıtlardan birinin kriptografik olarak güvenli bir varyantı" talebinizle ilgili olarak, lütfen RNCryptor'a bakın . Tam olarak istediğiniz şeyi yapmak için tasarlanmıştır (ve burada listelenen kodla ilgili sorunlara yanıt olarak oluşturulmuştur).

RNCryptor, tuzlu PBKDF2 kullanır, rastgele bir IV sağlar ve HMAC'yi (kendi tuzuyla PBKDF2'den üretilir. Senkronize ve asenkron çalışmayı destekler.


İlginç bir kod ve muhtemelen puan almaya değer. PBKDF2 için yineleme sayısı nedir ve HMAC'yi ne kadar hesaplıyorsunuz? Sadece şifrelenmiş verileri mi varsayıyorum? Sağlanan belgelerde bunu kolayca bulamadım.
Maarten Bodewes

Ayrıntılar için "En iyi uygulama güvenliği" ne bakın. İOS'ta 10k yineleme öneririm (iPhone 4'te ~ 80ms). Ve evet, HMAC'den-şifrele. V2.0'da güncel olduğundan emin olmak için muhtemelen bu gece "Veri biçimi" sayfasına bakacağım (ana dokümanlar güncel, ancak veri biçimi sayfasını revize edip etmediğimi hatırlayamıyorum).
Rob Napier

Ah, evet, belgelerde tur sayısını bulup koda baktım. Orada temizleme işlevleri ve ayrı HMAC ve şifreleme anahtarları görüyorum. Zaman izin verirse yarın daha derinlemesine bakmaya çalışacağım. Sonra puanları vereceğim.
Maarten Bodewes

5
NSData'ya şifreleyin ve bunu bir dizeye dönüştürmek için birçok Base64 kodlayıcıdan birini kullanın. Veriden dizeye kodlayıcı olmadan bir dizeden dizeye şifreleme yapmanın bir yolu yoktur.
Rob Napier

1
@Jack Avukatımın tavsiyesi üzerine (ihracata uyum yasası konusundaki uzmanlığımın son derece renkli ifadeleriyle anlatan…), artık ihracata uyum yasası konusunda tavsiyede bulunmuyorum. Avukatınızla görüşmeniz gerekecek.
Rob Napier

12

@QuinnTaylor'da cevabını güncellemek için biraz bekledim, ama yapmadığı için, işte cevap biraz daha net ve XCode7'ye (ve belki daha büyük) yüklenecek şekilde. Bunu bir Cocoa uygulamasında kullandım, ancak muhtemelen bir iOS uygulamasıyla da iyi çalışacaktır. ARC hatası yok.

AppDelegate.m veya AppDelegate.mm dosyanızdaki herhangi bir @implementation bölümünün önüne yapıştırın.

#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

Bu iki işlevi istediğiniz @ uygulama sınıfına yapıştırın. Benim durumumda, AppDelegate.mm veya AppDelegate.m dosyamda @implementation AppDelegate'i seçtim.

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

Not: 1. Şifre çözme sırasında, doldurma olduğunda çıktı boyutu giriş boyutundan daha küçük olacaktır (PKCS # 7). BufferSize değerini artırmak için bir neden yoktur, sadece şifrelenmiş veri boyutunu kullanın. 2. Bir tamponu malloc'ing ve sonra dataWithBytesNoCopysadece bir NSMutableDatawith tahsis etmek yerine bayt işaretçisi dataWithLengthiçin mutableBytesözelliği kullanın ve sonra onun lengthözelliğini ayarlayarak yeniden boyutlandırın . 3. Şifreleme için dizeyi doğrudan kullanmak çok güvensizdir, PBKDF2 tarafından oluşturulan gibi türetilmiş bir anahtar kullanılmalıdır.
2016

@zaph, değişiklikleri görebilmem için bir yere pastebin / pastie yapabilir misin? BTW, yukarıdaki kodda, Quinn Taylor'dan gördüğüm kodu sadece çalışmasını sağlamak için uyarladım. Halen bu işi öğreniyorum ve görüşleriniz benim için çok faydalı olacak.
Volomike

Bu SO cevabına bakın ve hatta minimum hata işleme sahiptir ve hem şifreleme hem de şifre çözme işlemini gerçekleştirir. Şifre çözmede arabelleği genişletmeye gerek yoktur, kazanılacak çok az şey olduğunda ek ile uzmanlaşmayan kod daha azdır. (Bu yapılmamalıdır) istenen boş değerlere sahip anahtar uzanan durumda sadece anahtarın bir değişken versiyonunu oluşturmak ve uzunluğunu ayarlamak: keyData.length = kCCKeySizeAES256;.
zaph

Bir dizeden bir anahtar oluşturmak için PBKDF2 kullanmak için bu SO yanıtına bakın .
zaph

@Volomike Bunu kullanırsam , iTunes-Connect'te Uyum Bilgilerini Dışa Aktar (YES) seçeneğini seçmeli miyim?
Jack

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.