6

私はこれで頭を壁にぶつけています。.NET で記述されていると思われる Web サービスに送信するために、ECB モードで 3DES を使用して 4 桁の「ピン」を暗号化するように iPhone アプリケーションをコーディングする必要があります。

+ (NSData *)TripleDESEncryptWithKey:(NSString *)key dataToEncrypt:(NSData*)encryptData {
NSLog(@"kCCKeySize3DES=%d", kCCKeySize3DES);
char keyBuffer[kCCKeySize3DES+1]; // room for terminator (unused)
bzero( keyBuffer, sizeof(keyBuffer) ); // fill with zeroes (for padding)

[key getCString: keyBuffer maxLength: sizeof(keyBuffer) encoding: NSUTF8StringEncoding];

// encrypts in-place, since this is a mutable data object
size_t numBytesEncrypted = 0;

size_t returnLength = ([encryptData length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);

// NSMutableData* returnBuffer = [NSMutableData dataWithLength:returnLength];
char* returnBuffer = malloc(returnLength * sizeof(uint8_t) );

CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt, kCCAlgorithm3DES , kCCOptionECBMode,
                                 keyBuffer, kCCKeySize3DES, nil,
                                 [encryptData bytes], [encryptData length], 
                                 returnBuffer, returnLength,
                                 &numBytesEncrypted);

if (ccStatus == kCCParamError) NSLog(@"PARAM ERROR");
else if (ccStatus == kCCBufferTooSmall) NSLog(@"BUFFER TOO SMALL");
else if (ccStatus == kCCMemoryFailure) NSLog(@"MEMORY FAILURE");
else if (ccStatus == kCCAlignmentError) NSLog(@"ALIGNMENT");
else if (ccStatus == kCCDecodeError) NSLog(@"DECODE ERROR");
else if (ccStatus == kCCUnimplemented) NSLog(@"UNIMPLEMENTED");

if(ccStatus == kCCSuccess) {
    NSLog(@"TripleDESEncryptWithKey encrypted: %@", [NSData dataWithBytes:returnBuffer length:numBytesEncrypted]);
    return [NSData dataWithBytes:returnBuffer length:numBytesEncrypted];
}
else 
    return nil;
} }

上記のコードを使用して暗号化された値を取得しますが、.NET Web サービスからの値と一致しません。

問題は、Web サービス開発者から提供された暗号化キーの長さが 48 文字であることだと思います。

iPhone SDK の定数「kCCKeySize3DES」が 24 であることを確認しました。そのため、commoncrypto API 呼び出しが提供されたキーの最初の 24 文字のみを使用していると思われますが、わかりません。

これは正しいです?

これを取得して正しい暗号化ピンを生成する方法はありますか? 暗号化前のデータ バイトを base64 エンコーディングに出力し、これを .NET コードから生成されたものと照合しようとしました (バイト配列出力を送ってくれた .NET 開発者の助けを借りて)。base64 でエンコードされていないバイト配列も、base64 でエンコードされた最終的な文字列も一致しません。

4

3 に答える 3

4

3DES は対称ブロック暗号です。3DES は 24 バイトのキーを使用して、8 バイト ブロックを別の 8 バイト ブロックに暗号化します。同じ 24 バイトのキーを使用すると、暗号化は元に戻すことができます (つまり、復号化できます)。

キーは、任意のバイト シーケンスです。それは「キャラクター」と同じではありません。特に、これらのバイトの 1 つを値ゼロにすることは完全に合法です。同様に、入力と出力は任意のバイトである可能性があります。

与えられたキーが「文字」で構成されている場合は、何らかの方法で適切なバイト シーケンスに変換する必要があります。48 文字の「キー文字列」を取得し、48 は正確に 24*2 であるため、もっともらしい推測は、キーが 16 進表記で指定されていることです。数字と「a」から「f」までの文字のみが含まれているかどうかを確認してください。

パディングについて: 3DES は 8 バイトのブロックのみを暗号化します。「メッセージ」が暗号化され、8 バイトとは異なる長さを持つ場合、3DES への呼び出しで暗号化できるように、メッセージをフォーマットし、分割し、処理するのが通例です。2 つのキーワードはpaddingchainingです。パディングとは、長さが適切 (8 の倍数など) になるように、末尾に余分なバイトを追加することです (これらのバイトを明確に削除できるようにするため)。連鎖とは、各 3DES 呼び出しに正確に何が入るかを決定することです (単純に、パディングされたメッセージを個別に暗号化されたブロックに分割することは「ECB」と呼ばれ、弱点があります)。

PIN コードに 4 桁が含まれている場合、これらの 4 桁が少なくとも 8 バイトになり、3DES に供給される方法について何らかの規則が必要です。iPhone がこのMacOS X のマニュアル ページで説明されているものと同様に動作する場合、コードの長さencryptDataが 8 の倍数でない限り、コードは正常に実行されません。これは、4 桁の PIN を 8 バイトのバッファーに変換する、表示されていないコードが、重要な変換を既に行っていることを意味します。たとえば、そのコードは 4 桁を 4 バイトに (ASCII エンコーディングを使用して) 配置し、他の 4 バイトをゼロに設定する場合があります。あるいは、そうすることに失敗するかもしれません。いずれにせよ、3DES への 64 の入力ビットのそれぞれが重要であり、サーバーとまったく同じ方法で取得する必要があります。そのコードも検査する必要があります。

于 2010-03-16T15:39:21.157 に答える
2

まあ、私はたくさんの読書とスタックオーバーフローに関するコメントでこれを解決することができました。いくつかの問題があります。.NET 開発者から与えられたキーは 48 文字でした。もちろん、これは 16 進文字列として読み取られ、代わりに 24 文字に変換される必要がありました。

これを行うコードを追加しました。完全なルーチンは次のとおりです。私たちの実装に非常に固有であるため、それが役立つかどうかはわかりません。

+ (NSString *)doCipher3DES:(NSString *)sTextIn key:(NSString *)sKey {
NSMutableData * dTextIn;
CCCryptorStatus ccStatus = kCCSuccess;

// need to add 4 zeros as sTextIn will be a 4 digit PIN
sTextIn = [sTextIn stringByAppendingString:@"0000"];

// convert to data
dTextIn = [[sTextIn dataUsingEncoding: NSASCIIStringEncoding] mutableCopy];           

// key will be a 48 char hex stream, so process it down to 24 chars
const char * bytes = [sKey cStringUsingEncoding: NSUTF8StringEncoding];
NSUInteger length = strlen(bytes);
unsigned char * r = (unsigned char *) malloc(length / 2 + 1);
unsigned char * index = r;

while ((*bytes) && (*(bytes +1))) {
    *index = strToChar(*bytes, *(bytes +1));
    index++;
    bytes+=2;
}
*index = '\0';

NSData *dKey = [NSData dataWithBytes: r length: length / 2];
free(r);

NSLog(@"doCipher3DES - key: %@", dKey);

uint8_t *bufferPtr1 = NULL;    
size_t bufferPtrSize1 = 0;    
size_t movedBytes1 = 0;    
uint8_t iv[kCCBlockSize3DES];    
memset((void *) iv, 0x0, (size_t) sizeof(iv));    
bufferPtrSize1 = ([sTextIn length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES -1);    
bufferPtr1 = malloc(bufferPtrSize1 * sizeof(uint8_t));    
memset((void *)bufferPtr1, 0x00, bufferPtrSize1);    

ccStatus = CCCrypt(kCCEncrypt, // CCOperation op    
                   kCCAlgorithm3DES, // CCAlgorithm alg    
                   kCCOptionECBMode, // CCOptions options    
                   (const void *)[dKey bytes], // const void *key    
                   kCCKeySize3DES, // size_t keyLength    
                   nil, // const void *iv    
                   (const void *)[dTextIn bytes], // const void *dataIn
                   [dTextIn length],  // size_t dataInLength    
                   (void *)bufferPtr1, // void *dataOut    
                   bufferPtrSize1,     // size_t dataOutAvailable 
                   &movedBytes1);      // size_t *dataOutMoved     

if (ccStatus == kCCParamError) NSLog(@"PARAM ERROR");
else if (ccStatus == kCCBufferTooSmall) NSLog(@"BUFFER TOO SMALL");
else if (ccStatus == kCCMemoryFailure) NSLog(@"MEMORY FAILURE");
else if (ccStatus == kCCAlignmentError) NSLog(@"ALIGNMENT");
else if (ccStatus == kCCDecodeError) NSLog(@"DECODE ERROR");
else if (ccStatus == kCCUnimplemented) NSLog(@"UNIMPLEMENTED");

NSString * sResult;    
NSData *dResult = [NSData dataWithBytes:bufferPtr1 length:movedBytes1];    

NSLog(@"doCipher3DES encrypted: %@", dResult);

sResult = [Base64 encode:dResult];    

return sResult; }

strToChar のコードは次のとおりです。

unsigned char strToChar (char a, char b) {
char encoder[3] = {'\0','\0','\0'};
encoder[0] = a;
encoder[1] = b;
return (char) strtol(encoder,NULL,16); }

これが誰かに役立つことを願っています...

于 2010-03-16T17:10:52.790 に答える
1

たぶん、パディングを使用する必要がありますか?オプションを次のように設定してみてください。

(kCCOptionPKCS7Padding | kCCOptionECBMode)
于 2010-03-16T13:55:26.940 に答える