iPhone上のNSStringのAES暗号化
-
05-07-2019 - |
質問
文字列を暗号化し、暗号化されたデータを含む別の文字列を返すために、誰かが私を正しい方向に向けることができますか? (AES256暗号化を試しています。)2つのNSStringインスタンスを取得するメソッドを作成します。1つは暗号化するメッセージで、もう1つは暗号化する「パスコード」です-生成する必要があると思います暗号化されたデータとともにパスコードが提供された場合に逆にできるように、パスコード付きの暗号化キー。メソッドは、暗号化されたデータから作成されたNSStringを返す必要があります。
この投稿の最初のコメントですが、これまでのところ運がありません。 Appleの CryptoExercise には確かに何かがありますが、私はそれを理解できません。 .. CCCrypt ですが、使用したすべてのケースで失敗しています。
暗号化された文字列も復号化できる必要がありますが、kCCEncrypt / kCCDecryptと同じくらい簡単であることを願っています。
解決
コードを投稿していないので、発生している問題を正確に知ることは困難です。ただし、リンク先のブログ投稿はかなりうまく機能しているようです...コンパイルエラーの原因となった CCCrypt()
の呼び出しごとの余分なコンマは別として、
この投稿に対する後のコメントには、この適応されたコードが含まれています。簡単です。 NSDataカテゴリのコードを含めると、次のように書くことができます:(注: printf()
呼び出しは、さまざまな時点でのデータの状態を示すためだけです—実際のアプリケーションでは、そのような値を出力することは意味がありません。)
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;
}
このコードと、暗号化されたデータが常にNSStringにうまく変換されないという事実を考えると、必要な機能を順方向と逆方向にラップする2つのメソッドを記述する方が便利かもしれません...
- (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];
}
これは間違いなくSnow Leopardで機能し、 @Boz はCommonCryptoがiPhoneのコアOSの一部であると報告しています。 10.4および10.5には / usr / include / CommonCrypto
がありますが、10.5には CCCryptor.3cc
のマニュアルページがあり、10.4にはないため、YMMVです。
編集: Base64エンコードを使用して暗号化されたデータバイトを表す方法については、このフォローアップの質問をご覧ください。安全なロスレス変換を使用した文字列として(必要な場合)。
他のヒント
Jeff LaMarcheのブログおよびスタックオーバーフローに関するクインテイラーによるヒント。
カテゴリを使用してNSDataを拡張してAES256暗号化を提供し、暗号化されたデータを文字列に安全にBASE64エンコードするNSStringの拡張機能も提供します。
文字列の暗号化の使用法を示す例は次のとおりです。
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] );
ここから完全なソースコードを入手してください:
すべての役立つヒントをありがとう!
-マイケル
@owlstead、「与えられた回答の1つの暗号的に安全なバリアント」に対するあなたのリクエストに関して、 RNCryptor をご覧ください。あなたが要求していることを正確に行うように設計されています(そして、ここにリストされたコードの問題に対応して構築されました)。
RNCryptorはPBKDF2とソルトを使用し、ランダムIVを提供し、HMAC(PBKDF2から生成され、独自のソルトを使用します。同期および非同期操作をサポートします。
@QuinnTaylorで回答を更新するのを少し待ちましたが、更新しなかったので、ここで回答をもう少し明確に、XCode7(およびそれ以上)でロードする方法で示します。これをCocoaアプリケーションで使用しましたが、iOSアプリケーションでも問題なく動作する可能性があります。 ARCエラーはありません。
AppDelegate.mまたはAppDelegate.mmファイルの@implementationセクションの前に貼り付けます。
#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
これら2つの関数を必要な@implementationクラスに貼り付けます。私の場合、AppDelegate.mmまたはAppDelegate.mファイルで@implementation AppDelegateを選択しました。
- (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];
}