Возникли проблемы с расшифровкой в ​​C # что-то зашифрованное на iPhone с помощью RSA - PullRequest
8 голосов
/ 16 июля 2009

Я провел два дня на этом и прочесал все источники в моем распоряжении, так что это последнее средство.

У меня есть сертификат X509, открытый ключ которого я хранил в цепочке для ключей iPhone (только на симуляторе). На стороне ASP.NET у меня есть сертификат в хранилище сертификатов с закрытым ключом. Когда я шифрую строку на iPhone и дешифрую ее на сервере, я получаю CryptographicException «Плохие данные». Я попробовал Array.Reverse, предложенный на странице RSACryptoServiceProvider, но это не помогло.

Я сравнил строки base-64 с обеих сторон, и они равны. Я сравнил необработанные байтовые массивы после декодирования, и они тоже равны. Если я шифрую на сервере с помощью открытого ключа, байтовый массив отличается от версии iPhone и легко расшифровывается с помощью закрытого ключа. Необработанная строка открытого текста состоит из 115 символов, поэтому она не превышает 256-байтового ограничения моего 2048-битного ключа.

Вот метод шифрования iPhone (в значительной степени дословно из примера приложения CryptoExercise wrapSymmetricKey):

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

А вот метод расшифровки C # на стороне сервера:

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

Я подозревал, что между платформами возникли проблемы с кодированием. Я знаю, что один - с прямым порядком байтов, а другой - с прямым порядком байтов, но я не знаю достаточно, чтобы сказать, что и как преодолеть разницу. Mac OS X, Windows и iPhone все с прямым порядком байтов, так что это не проблема.

Новая теория: если вы установите для логического значения заполнения OAEP значение false, по умолчанию используется заполнение PKCS # 1 1.5. SecKey имеет только SecPadding определений PKCS1, PKCS1MD2, PKCS1MD5 и PKCS1SHA1. Возможно, Microsoft PKCS # 1 1.5! = Apple PKCS1, и поэтому заполнение влияет на двоичный вывод шифрования. Я попытался использовать kSecPaddingPKCS1 с fOAEP, установленным на false, и он все еще не работал. По-видимому, kSecPaddingPKCS1 эквивалентно для PKCS # 1 1.5. Вернуться к чертежной доске по теориям & hellip;

Другие недавно опробованные теории:

  1. Сертификат на iPhone (файл .cer) не совсем совпадает с пакетом PKCS # 12 на сервере (файл .pfx), поэтому он никогда не сможет работать. Установленный файл .cer в другом хранилище сертификатов и строка, зашифрованная на сервере, округлены;
  2. Преобразование в base-64 и процесс POSTing на сервере привели к странности, которой не было в одном и том же классе, поэтому я сначала попробовал URLEncoding / Decoding, а затем отправил необработанный двоичный файл с iPhone, проверил, что он равен, и получил те же плохие данные;
  3. Моя исходная строка была 125 байтов, поэтому я подумал, что она может быть усечена в UTF-8 (длинный кадр), поэтому я обрезал ее до 44-байтовой строки без результата;
  4. Оглянулся на библиотеку System.Cryptography, чтобы убедиться, что я использовал соответствующий класс, и обнаружил `RSAPKCS1KeyExchangeDeformatter`, стал взволнован новыми перспективами и подавлен, когда он вел себя точно так же.

Успех!

Оказалось, что у меня была какая-то грязь в моей цепочке для ключей на iPhone Simulator, который, так сказать, мутил воду. Я удалил Брелок БД на ~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db, чтобы он был заново создан, и он работал нормально. Спасибо за вашу помощь. Цифры это было бы чем-то простым, но неочевидным. (Две вещи, которые я узнал: 1) удаление приложения из симулятора не очищает его записи в цепочке для ключей и 2) периодически запускается абсолютно заново.)

ПРИМЕЧАНИЕ. Общий путь к файлу цепочки для ключей зависит от версии iOS: ~ / Библиотека / Поддержка приложений / iPhone Simulator / [версия] /Library/Keychains/keychain-2-debug.db например., ~ / Библиотека / Поддержка приложений / iPhone Simulator / 4.3 / Библиотека / Брелки / keychain-2-debug.db

Ответы [ 5 ]

3 голосов
/ 18 июля 2009

Ну ... первый шаг (как вы говорите, вы сделали) - это зашифровать одни и те же сообщения с одинаковыми векторами инициализации, используя как iPhone, так и реализацию C #. Вы должны получить тот же результат. Вы сказали, что нет, поэтому есть проблема.

Это означает либо:

  • Неправильная реализация RSA для iPhone.
  • .NET реализация RSA неверна.
  • Файлы ключей различны (или интерпретируются по-разному).

Я бы предположил, что первые два маловероятны, однако они возможны удаленно.

Вы утверждаете: «Установленный .cer-файл в другом хранилище сертификатов и зашифрованная на сервере строка, перевернутая просто отлично» ... это ничего не доказывает: все это доказывает то, что при наличии определенного случайного набора чисел вы можете зашифровать / успешно расшифровать на одной платформе. Вы не гарантируете, что обе платформы видят один и тот же набор случайных чисел.

Так что я предлагаю вам довести его до самого низкого возможного уровня здесь. Проверьте прямые (байтовые массивы) входы и выходы шифрования на обеих платформах. Если при одинаковых (двоичных) входах вы не получаете одинаковый вывод, значит, у вас проблема с платформой. Я думаю, что это маловероятно, поэтому я предполагаю, что вы обнаружите, что IV интерпретируются по-разному.

1 голос
/ 20 июля 2009

это мой первый ответ на stackoverflow, поэтому, пожалуйста, прости меня, если я делаю это неправильно!

Я не могу дать вам полный ответ, однако у меня были очень похожие проблемы, когда я пытался интегрироваться с PHP - кажется, что формат файлов сертификатов Apple немного отличается от того, который ожидает другое программное обеспечение (включая openssl) .

Вот как я расшифровываю зашифрованную подпись в PHP - я на самом деле извлекаю модуль и PK из переданного открытого ключа вручную и использую это для RSA, вместо того, чтобы пытаться импортировать ключ:

// 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.  

Цель-c, которая генерирует этот открытый ключ:

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

Использование SecKeyWrapper из примера проекта Apple CryptoExercise (вы можете просмотреть файл здесь: https://developer.apple.com/iphone/library/samplecode/CryptoExercise/listing15.html)

Надеюсь, это поможет?

0 голосов
/ 16 июля 2009

Поскольку вы контролируете обе стороны, моя рекомендация (если вы не можете заставить алгоритмы шифрования библиотеки работать вместе на двух платформах) заключалась бы в том, чтобы написать шифрование самостоятельно для обеих сторон, используя один и тот же алгоритм.

Таким образом, у вас есть контроль, и вы сможете отлаживать внутреннюю часть шифрования, чтобы увидеть, что происходит не так.

Это последнее средство (конечно), но, вероятно, заняло бы меньше времени, чем три дня, которые вы уже провели, и с высокой вероятностью успеха

НТН

0 голосов
/ 17 июля 2009

Поможет ли это вам?

Асимметричное шифрование ключей с .NET & C #

  • Извините за короткий пост, ограничения по времени и все. Во всяком случае, видел ваш запрос Twitter о помощи .. это показывает, как я сделал это с PHP и расшифровывается на .NET, simliar. Я заметил, что ваш класс дешифрования немного отличается от моего, поэтому эта статья может помочь.
0 голосов
/ 16 июля 2009

Полагаю, вы сами ответили на вопрос. Скорее всего, проблема заключается в порядке байтов.

Это возможный способ написания методов двустороннего преобразования:

short convert_short(short in)
{
 short out;
 char *p_in = (char *) &in;
 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 *) &in;
 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;
} 

Это может быть хорошим ресурсом для вас (кроме Википедии): http://betterexplained.com/articles/understanding-big-and-little-endian-byte-order/

...