Я провел два дня на этом и прочесал все источники в моем распоряжении, так что это последнее средство.
У меня есть сертификат 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;
Другие недавно опробованные теории:
- Сертификат на iPhone (файл .cer) не совсем совпадает с пакетом PKCS # 12 на сервере (файл .pfx), поэтому он никогда не сможет работать. Установленный файл .cer в другом хранилище сертификатов и строка, зашифрованная на сервере, округлены;
- Преобразование в base-64 и процесс POSTing на сервере привели к странности, которой не было в одном и том же классе, поэтому я сначала попробовал URLEncoding / Decoding, а затем отправил необработанный двоичный файл с iPhone, проверил, что он равен, и получил те же плохие данные;
- Моя исходная строка была 125 байтов, поэтому я подумал, что она может быть усечена в UTF-8 (длинный кадр), поэтому я обрезал ее до 44-байтовой строки без результата;
- Оглянулся на библиотеку 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