2009-07-15 33 views
8

Tôi đã dành hai ngày cho đến nay và chải kỹ mọi nguồn theo ý của tôi, vì vậy đây là phương sách cuối cùng.Gặp sự cố khi giải mã trong C# một cái gì đó được mã hóa trên iPhone bằng RSA

Tôi có chứng chỉ X509 có khóa công khai tôi đã lưu trữ trong keychain của iPhone (chỉ mô phỏng tại thời điểm này). Về phía ASP.NET, tôi đã có chứng chỉ trong cửa hàng cert với một khóa riêng. Khi tôi mã hóa một chuỗi trên iPhone và giải mã nó trên máy chủ, tôi nhận được CryptographicException "Dữ liệu không hợp lệ". Tôi đã thử các Array.Reverse đề nghị trong trang RSACryptoServiceProvider trên một longshot, nhưng nó đã không giúp đỡ.

Tôi đã so sánh các chuỗi base-64 ở cả hai bên và chúng bằng nhau. Tôi đã so sánh các mảng byte thô sau khi giải mã và chúng cũng bằng nhau. Nếu tôi mã hóa trên máy chủ bằng cách sử dụng khóa công khai, mảng byte khác với phiên bản của iPhone và dễ dàng giải mã bằng khóa riêng. Chuỗi thô thô là 115 ký tự vì vậy nó nằm trong giới hạn 256 byte của khóa 2048-bit của tôi.

Dưới đây là các phương pháp mã hóa iPhone (khá nhiều nguyên văn từ wrapSymmetricKey phương pháp 's CryptoExercise sample app):

+ (NSData *)encrypt:(NSString *)plainText usingKey:(SecKeyRef)key error:(NSError **)err 
{ 
    size_t cipherBufferSize = SecKeyGetBlockSize(key); 
    uint8_t *cipherBuffer = NULL; 
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t)); 
    memset((void *)cipherBuffer, 0x0, cipherBufferSize); 
    NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding]; 
    OSStatus status = SecKeyEncrypt(key, kSecPaddingNone, 
           (const uint8_t *)[plainTextBytes bytes], 
           [plainTextBytes length], cipherBuffer, 
           &cipherBufferSize); 
    if (status == noErr) 
    { 
     NSData *encryptedBytes = [[[NSData alloc] 
        initWithBytes:(const void *)cipherBuffer 
        length:cipherBufferSize] autorelease]; 
     if (cipherBuffer) 
     { 
      free(cipherBuffer); 
     } 
     NSLog(@"Encrypted text (%d bytes): %@", 
        [encryptedBytes length], [encryptedBytes description]); 
     return encryptedBytes; 
    } 
    else 
    { 
     *err = [NSError errorWithDomain:@"errorDomain" code:status userInfo:nil]; 
     NSLog(@"encrypt:usingKey: Error: %d", status); 
     return nil; 
    } 
} 

Và đây là server-side phương pháp C# giải mã:

private string Decrypt(string cipherText) 
{ 
    if (clientCert == null) 
    { 
     // Get certificate 
     var store = new X509Store(StoreName.My, StoreLocation.LocalMachine); 
     store.Open(OpenFlags.ReadOnly); 
     foreach (var certificate in store.Certificates) 
     { 
      if (certificate.GetNameInfo(X509NameType.SimpleName, false) == CERT) 
      { 
       clientCert = certificate; 
       break; 
      } 
     } 
    } 

    using (var rsa = (RSACryptoServiceProvider)clientCert.PrivateKey) 
    { 
     try 
     { 
      var encryptedBytes = Convert.FromBase64String(cipherText); 
      var decryptedBytes = rsa.Decrypt(encryptedBytes, false); 
      var plaintext = Encoding.UTF8.GetString(decryptedBytes); 
      return plaintext; 
     } 
     catch (CryptographicException e) 
     { 
      throw(new ApplicationException("Unable to decrypt payload.", e)); 
     } 
    } 
} 

nghi ngờ của tôi là đã có một số vấn đề về mã hóa giữa các nền tảng. Tôi biết rằng một người là người lớn tuổi và người kia là người ít tuổi nhưng tôi không biết đủ để nói cái nào hay cách nào để khắc phục sự khác biệt. Hệ điều hành Mac OS X, Windows và iPhone đều là một chút nhỏ bé nên không phải là vấn đề.

Lý thuyết mới: nếu bạn đặt đệm OAEP Boolean thành false, đệm mặc định là đệm PKCS # 1 1.5. SecKey chỉ có định nghĩa SecPadding của PKCS1, PKCS1MD2, PKCS1MD5PKCS1SHA1. Có lẽ PKCS # 1 của Microsoft! = PKCS1 của Apple và do đó padding ảnh hưởng đến đầu ra nhị phân của mã hóa. Tôi đã thử sử dụng kSecPaddingPKCS1 với fOAEP được đặt thành false và nó vẫn không hoạt động. Rõ ràng, kSecPaddingPKCS1equivalent tới PKCS # 1 1.5. Quay lại bảng vẽ về lý thuyết & hellip;

lý thuyết khác mới thử:

  1. Giấy chứng nhận trên iPhone (file cer) không phải là chính xác giống như các PKCS # 12 bó trên máy chủ (file pfx) và vì vậy nó không bao giờ có thể làm việc. Tệp .cer được cài đặt trong kho lưu trữ cert khác nhau và chuỗi được mã hóa máy chủ được làm tròn tốt;
  2. Chuyển đổi thành cơ sở 64 và hành động POST thành máy chủ dẫn đến sự kỳ quặc không có trong cùng một lớp, vì vậy tôi đã thử một số URLEncoding/Decoding và sau đó đăng nhị phân thô từ iPhone, xác minh rằng nó bằng nhau và có cùng dữ liệu xấu;
  3. Chuỗi gốc của tôi là 125 byte vì vậy tôi nghĩ rằng nó có thể cắt ngắn trong UTF-8 (ảnh dài) vì vậy tôi đã cắt nó xuống chuỗi 44 byte mà không có kết quả;
  4. Nhìn lại thư viện System.Cryptography để đảm bảo rằng tôi đang sử dụng một lớp thích hợp và phát hiện ra `RSAPKCS1KeyExchangeDeformatter`, trở nên phấn khởi với triển vọng mới, và bị từ chối khi nó hoạt động giống hệt nhau.

Thành công!

Hóa ra là tôi đã có một số chiếc cruft trong Keychain của mình trên Trình mô phỏng iPhone đang làm ngập nước, vì vậy để nói. Tôi đã xóa Keychain DB tại ~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db để làm cho nó được tái tạo và nó hoạt động tốt. Cảm ơn vì tất cả sự giúp đỡ của bạn. Số liệu nó sẽ là một cái gì đó đơn giản nhưng không rõ ràng. (Hai điều tôi đã học được: 1) gỡ cài đặt ứng dụng khỏi trình mô phỏng không xóa các mục Keychain và 2) bắt đầu hoàn toàn mới định kỳ.)

LƯU Ý: Đường dẫn chung cho tệp keychain phụ thuộc vào phiên bản iOS: ~/Library/Application Support/iPhone Simulator/[phiên bản] /Library/Keychains/keychain-2-debug.db ví dụ: ~/Library/Application Support/iPhone Simulator/4.3/Library/Keychains/keychain-2-debug .db

+0

Thời gian để nhận tiền thưởng! – bbrown

Trả lời

3

Vâng ... bước đầu tiên (như bạn nói bạn đã làm) là mã hóa cùng một thông báo với cùng vectơ khởi tạo bằng cách sử dụng cả iPhone và triển khai C#. Bạn sẽ nhận được cùng một đầu ra. Bạn nói bạn đã không, vì vậy có một vấn đề.

này có nghĩa là:

  • Việc thực hiện iPhone của RSA là không chính xác.
  • Việc triển khai .NET của RSA không chính xác.
  • Các tệp chính khác nhau (hoặc được diễn giải khác nhau).

Tôi cho rằng hai trường hợp đầu tiên không chắc chắn, tuy nhiên chúng có thể từ xa.

Bạn ghi rõ: "Tệp .cer được cài đặt trong kho lưu trữ chứng chỉ khác nhau và chuỗi được mã hóa máy chủ được làm tròn tốt" ... điều này không chứng minh được bất kỳ điều gì: tất cả điều này được chứng minh là có một bộ số ngẫu nhiên cụ thể mà bạn có thể mã hóa/giải mã thành công trên một nền tảng. Bạn không đảm bảo rằng cả hai nền tảng đều nhìn thấy cùng một tập hợp các số ngẫu nhiên.

Vì vậy, tôi khuyên bạn nên hạ thấp xuống mức thấp nhất có thể tại đây. Kiểm tra đầu vào và đầu ra trực tiếp (mảng byte) của mã hóa trên cả hai nền tảng. Nếu với đầu vào chính xác (nhị phân) bạn không nhận được cùng một đầu ra, thì bạn có vấn đề nền tảng. Tôi nghĩ rằng điều này là không thể, vì vậy tôi đoán bạn sẽ thấy rằng các IV đang được giải thích khác nhau.

+0

Cảm ơn bạn đã trả lời! Tôi có thể tìm thấy hư không trong các thư viện .NET hoặc Cocoa, nơi bạn có thể thiết lập một IV cho mã hóa RSA hoặc giải mã. Tôi nghĩ IV chỉ áp dụng cho các thuật toán đối xứng. Tôi cũng sẽ không nghĩ rằng 1 hoặc 2 là có thể, nhưng tôi cũng không thể hiểu rằng khóa công khai của chứng chỉ X509 sẽ được diễn giải khác nhau trên hai nền tảng. Nhưng tôi lấy chứng chỉ từ điện thoại và mở nó vào máy chủ và nó đã thành công ở đó nên tôi không biết phải nghĩ gì nữa. Tôi sẽ xem xét các chi tiết khóa công khai trong mỗi nền tảng để đảm bảo rằng chúng giống nhau, bbrown

+0

nhưng tôi khá chắc chắn rằng tôi không nên ngạc nhiên khi cùng một khóa công khai có thể tạo ra kết quả khác nhau cho cùng một đầu vào trên các thẻ khác nhau. Đó là mục đích của PKCS # 1 padding, một cái gì đó một kỹ sư của Apple đã xác nhận: http://lists.apple.com/archives/apple-cdsa/2009/Jul/msg00032.html – bbrown

+0

Xin lỗi - khi tôi nói IV tôi đã có lẽ lạm dụng thuật ngữ - nó trễ ở đây :) Tôi có nghĩa là về cơ bản khóa mã hóa: dữ liệu bạn nhận được từ phương thức ExportParameters trong .NET hoặc nội dung của tham số 'khóa' của bạn trên iPhone. Tôi nghĩ rằng nó sẽ là giá trị đảm bảo họ là như nhau (không bao gồm phần tư nhân của khóa học). –

-2

Tôi tin rằng bạn đã tự trả lời câu hỏi. Vấn đề chắc chắn nhất nằm trong endianness.

Đây là một cách có thể viết phương pháp chuyển đổi hai chiều:

short convert_short(short in) 
{ 
short out; 
char *p_in = (char *) ∈ 
char *p_out = (char *) &out; 
p_out[0] = p_in[1]; 
p_out[1] = p_in[0]; 
return out; 
} 

long convert_long(long in) 
{ 
long out; 
char *p_in = (char *) ∈ 
char *p_out = (char *) &out; 
p_out[0] = p_in[3]; 
p_out[1] = p_in[2]; 
p_out[2] = p_in[1]; 
p_out[3] = p_in[0]; 
return out; 
} 

Đây có thể là một nguồn lực tốt cho bạn (trừ wikipedia): http://betterexplained.com/articles/understanding-big-and-little-endian-byte-order/

+0

Thật tuyệt vời khi nghe và cảm ơn phản hồi! Tha thứ cho sự thiếu hiểu biết của tôi, nhưng chính xác tôi sẽ sử dụng những phương pháp chuyển đổi này như thế nào? Tôi cho rằng họ đang ở trên iPhone nên tôi sẽ cắm nó vào phương pháp của tôi ở đâu? – bbrown

+0

Chỉ ra rằng sự kết thúc giữa các nền tảng là một di tích của các ngày PowerPC. Mac OS X, được xây dựng dựa trên kiến ​​trúc x86, rất nhỏ bé - giống như Windows. IPhone, trên kiến ​​trúc ARM, cũng rất ít người gần như tôi có thể xác định từ các tìm kiếm. – bbrown

+0

Endianness sẽ không phải là một vấn đề ở đây, anyway. Tất cả các hoạt động của bạn liên quan đến byte thô, không phải số nhiều byte. –

-3

Bởi vì bạn kiểm soát cả hai bên, tôi đề nghị (nếu bạn không thể có được các thuật toán mã hóa thư viện để làm việc cùng nhau trên hai nền tảng) sẽ được viết mã hóa chính mình trên cả hai mặt, bằng cách sử dụng cùng một thuật toán.

Bằng cách đó, bạn có quyền kiểm soát và có thể gỡ lỗi nội bộ mã hóa để xem điều gì đang xảy ra.

Nó là một phương sách cuối cùng (tất nhiên) nhưng có lẽ đã ít thời gian hơn ba ngày mà bạn đã trải qua rồi, và có cơ hội thành công cao

HTH

+4

Với tất cả sự tôn trọng, tôi nghĩ rằng tôi nên sử dụng các tính năng bảo mật của khung tương ứng thay vì tự mình cuộn. Đầu tiên, việc thực hiện của họ là thử nghiệm chiến đấu và đã thông qua các đánh giá bảo mật mà tôi sẽ không có. Thứ hai, tôi là lập trình viên C# và Objective-C với các kỹ năng mật mã được thừa nhận giới hạn. Thứ ba, tôi nghĩ rằng tôi thực sự gần gũi về điều này và vấn đề là hòa tan. Cuối cùng, số lượng câu hỏi về SO (và các nơi khác) về tình huống này cho thấy rằng có nhiều câu hỏi đã bị vật lộn không thành công và do đó sẽ tốt hơn nếu có sẵn một giải pháp. – bbrown

+5

Um, không. Không ai trong tâm trí của họ nên triển khai thuật toán RSA trừ khi (a) không có sẵn cho nền tảng này, (b) họ là một nhà mật mã chuyên nghiệp, giàu kinh nghiệm, và (c) họ sẵn sàng bỏ ngân sách nhiều thời gian cho xem xét và kiểm tra mã. Việc triển khai mã hóa phức tạp, dễ bị các vấn đề tinh tế và rất nhạy cảm vì mọi người sẽ chủ động tấn công họ để tìm kiếm lợi nhuận. –

0

Will này giúp bạn ?

Asymmetric Key Encryption w/ .NET & C#

  • Xin lỗi vì bài viết ngắn, hạn chế thời gian và tất cả. Dù sao, thấy yêu cầu Twitter của bạn để được giúp đỡ .. điều này cho thấy làm thế nào tôi đã làm điều này với PHP và giải mã trên. NET, simliar. Tôi nhận thấy lớp giải mã của bạn hơi khác so với tôi, vì vậy bài viết này có thể hữu ích.
+0

Đáng buồn thay, OpenSSL không có sẵn trên iPhone để nó không giúp tôi. Nhưng cảm ơn vì đã cố gắng! – bbrown

+0

Điểm tôi đang làm là thử thói quen giải mã để phù hợp với tôi.Tôi thấy 509Certificate2 tốt hơn. –

+0

Đó là điều xấu của tôi. Trong ví dụ mã trên, có một biến cá thể riêng được gọi là "clientCert" có kiểu X509Certificate2. Tôi không đưa nó vào danh sách. Xin lỗi về điều đó ... – bbrown

1

đây là câu trả lời đầu tiên của tôi về stackoverflow, vì vậy hãy tha thứ cho tôi nếu tôi làm sai!

Tôi không thể cung cấp cho bạn câu trả lời hoàn chỉnh, tuy nhiên tôi có vấn đề tương tự khi cố gắng tích hợp với PHP - có vẻ như định dạng tệp chứng chỉ của Apple hơi khác so với phần mềm khác mong đợi (bao gồm openssl).

Đây là cách tôi giải mã chữ ký được mã hóa trong PHP - Tôi thực sự trích xuất các mô đun và PK từ khóa công khai truyền bằng tay và sử dụng cho những thứ RSA, chứ không phải cố gắng để nhập khẩu chính:

// Public key format in hex (2 hex chars = 1 byte): 
//30480241009b63495644db055437602b983f9a9e63d9af2540653ee91828483c7e302348760994e88097d223b048e42f561046c602405683524f00b4cd3eec7e67259c47e90203010001 
//<IGNORE><--------------------------------------------- MODULUS --------------------------------------------------------------------------><??><PK> 
// We're interested in the modulus and the public key. 
// PK = Public key, probably 65537 

// First, generate the sha1 of the hash string: 
$sha1 = sha1($hashString,true); 

// Unencode the user's public Key: 
$pkstr = base64_decode($publicKey); 
// Skip the <IGNORE> section: 
$a = 4; 
// Find the very last occurrence of \x02\x03 which seperates the modulus from the PK: 
$d = strrpos($pkstr,"\x02\x03"); 
// If something went wrong, give up: 
if ($a == false || $d == false) return false; 
// Extract the modulus and public key: 
$modulus = substr($pkstr,$a,($d-$a)); 
$pk = substr($pkstr,$d+2); 

// 1) Take the $signature from the user 
// 2) Decode it from base64 to binary 
// 3) Convert the binary $pk and $modulus into (very large!) integers (stored in strings in PHP) 
// 4) Run rsa_verify, from http://www.edsko.net/misc/rsa.php 
$unencoded_signature = rsa_verify(base64_decode($signature), binary_to_number($pk), binary_to_number($modulus), "512"); 

//Finally, does the $sha1 we calculated match the $unencoded_signature (less any padding bytes on the end)? 
return ($sha1 == substr($unencoded_signature,-20)); // SHA1 is only 20 bytes, whilst signature is longer than this. 

mục tiêu-c mà tạo ra khóa công khai này là:

NSData * data = [[SecKeyWrapper sharedWrapper] getPublicKeyBits]; 
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Public-Key"]; 
data = [[SecKeyWrapper sharedWrapper] getSignatureBytes:[signatureData dataUsingEncoding:NSUTF8StringEncoding]]; 
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Signature"]; 

Sử dụng SecKeyWrapper từ CryptoExercise dụ dự án của Apple (bạn có thể xem các tập tin ở đây: https://developer.apple.com/iphone/library/samplecode/CryptoExercise/listing15.html)

Tôi hy vọng điều này sẽ giúp ích?

+0

Như Jon Grant đã đề xuất, tôi sẽ kiểm tra các bit khóa công khai nhưng thực tế là iPhone đang đọc chứng chỉ X509 đi kèm với ứng dụng (không được tạo trên chính điện thoại) và tôi thấy không thể tin được rằng điện thoại sẽ đọc công khai chìa khóa theo cách khác. Tôi sẽ giải quyết điều này bằng cách kiểm tra các byte, nhưng tôi sẽ rất ngạc nhiên nếu đó là trường hợp. – bbrown

Các vấn đề liên quan