Расширения файлов
". Key" и ".cer" ни в коей мере не являются однозначной спецификацией кодирования ключей. Однако вполне вероятно, что файл «.cer» является сертификатом X.509, который содержит (среди прочего) открытый ключ; поэтому вы можете использовать классы X509Certificate
и X509Certificate2
(в пространстве имен System.Security.Cryptography.X509Certificates
) для декодирования сертификата и извлечения открытого ключа.
Однако вам нужен закрытый ключ, а не открытый ключ. Документация MSDN по X509Certificate2 весьма запутана, поскольку в ней используется термин «сертификат» для обозначения либо публичной части (что имеется в вашем файле «.cer»), либо объединения публичной и приватной частей, в виде одного файла (в формате, который MSDN описывает как «PKCS7 (Authenticode)»).
Закодированный закрытый ключ RSA обычно следует формату, описанному в PKCS # 1 , который не является сложным, но все же основан на ASN.1 , использование которого требует некоторой осторожности , Иногда такие ключи RSA оборачиваются в большую структуру, которая также определяет тип ключа (то есть, что ключ предназначен для RSA); подробности смотрите PKCS # 8 . Кроме того, оба вида кодировки клавиш обычно представлены в формате PEM: это Base64 с заголовком (-----BEGIN RSA PRIVATE KEY-----
) и нижним колонтитулом. Конечно, ваш закрытый ключ может быть в любом формате (расширение ".key" не слишком информативно). Опционально, PKCS # 8 и PEM могут быть симметрично зашифрованы (с помощью ключа, полученного из пароля). Существует также формат PKCS # 12 , который можно рассматривать как формат архива для коллекции сертификатов и секретных ключей, охватывающий предыдущие форматы; PKCS # 12 включает в себя множество уровней шифрования и известен в мире Microsoft под названием «PFX» (или «файл сертификата», который продолжает сбивать с толку).
Расшифровка всех этих форматов возможна с небольшим количеством кода, но в этот момент рекомендуется использовать библиотеку, которая уже выполняет такую работу, вместо того, чтобы использовать свою собственную. Надувной замок - обычный подозреваемый для этой работы.
Инструмент командной строки OpenSSL может помочь вам преобразовать некоторые форматы ключей и сертификатов.
Редактировать: если ваш закрытый ключ имеет формат PKCS # 8 DER и не защищен паролем (PKCS # 8 может это сделать), то вы можете декодировать его с помощью относительно простой код DER - это набор правил для преобразования структурированных данных в последовательность байтов. Элемент данных кодируется как три последовательные части:
- тег , который сообщает, какое значение это
- a длина , которая кодирует количество байтов в третьей части
- a значение , которое должно интерпретироваться как указано тегом
отсюда и название "TLV" (как "Tag, Length, Value"). Некоторые элементы сами являются структурами, содержащими подэлементы, и в этом случае значение заключается в объединении кодировок подэлементов, каждый из которых имеет свой собственный тег, длину и значение.
Метка обычно представляет собой один байт; для ключей PKCS # 8 и RSA вас интересуют теги 0x30 (для «SEQUENCE», то есть элемента с подэлементами), 0x02 («INTEGER»: целочисленное значение) и 0x04 («OCTET STRING»: блоб) .
Длина кодируется как:
- один байт значения n от 0 до 127 (включительно): это кодирует длину n ;
- байт значения n , равного или превышающего 129, за которым следуют ровно n-128 байтов, которые кодируют длину в формате с прямым порядком байтов. Например, длина 324 будет закодирована как три байта: 0x82 0x01 0x44. Это читается как: «0x82 - это 128 + 2, поэтому я должен прочитать два дополнительных байта; длина - 256 * 0x01 + 0x44 = 324».
Для INTEGER значение должно быть интерпретировано с подписанным соглашением с прямым порядком байтов (первый байт является наиболее значимым, а старший бит первого байта определяет знак целого числа; для RSA все значения положительны, поэтому первый байт должен иметь значение от 0 до 127). Обратите внимание, что System.Numerics.BigInteger
в .NET 4.0 имеет конструктор, который может декодировать группу байтов, но он ожидает их в соглашении с прямым порядком байтов, а не с прямым порядком байтов, поэтому вам придется изменить порядок байтов в обратном порядке.
Структура PKCS # 8:
PrivateKeyInfo ::= SEQUENCE {
version Version,
privateKeyAlgorithm AlgorithmIdentifier,
privateKey OCTET STRING,
attributes [0] Attributes OPTIONAL
}
Version ::= INTEGER { v1(0) }
Это нотация ASN.1. Здесь следует понимать, что объект является элементом SEQUENCE: он кодируется как тег SEQUENCE (0x30), затем длина ( n ), затем значение ( n байт). , именно так). Затем значение состоит из последовательности закодированных элементов, каждый в формате TLV. Первым элементом является INTEGER, который должен иметь числовое значение 0 при нормальных условиях (ноль кодируется как «0x02 0x01 0x00»). Второй элемент - это AlgorithmIdentifier
, который я здесь не буду описывать; на самом деле это SEQUENCE, и он идентифицирует тип ключа (здесь следует сказать «это ключ RSA»); просто прочитайте тег (должен быть 0x30), затем длину и пропустите значение. Третий элемент - это OCTET STRING: тег 0x04, длина m и значение m байтов. Это то, что нас интересует. Это значение, которое является содержанием OCTET STRING, должно быть извлечено; мы расшифруем его в следующем абзаце. Четвертый элемент PrivateKeyInfo
SEQUENCE является необязательным (его может вообще не быть и обычно не будет), и его можно использовать для кодирования различных расширений этого формата.
Предположим, что вы извлекли содержимое OCTET STRING. Это последовательность байтов, которая фактически является DER-кодировкой структуры, которая в ASN.1 выглядит следующим образом:
RSAPrivateKey ::= SEQUENCE {
version INTEGER,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
-- otherPrimeInfos must be absent if version is two-prime,
-- present if version is multi-prime.
}
Таким образом, содержимое OCTET STRING должно начинаться с 0x30 (тег SEQUENCE), затем с длины ( r ), затем r байтов. Эти r байтов являются кодировками девяти целых чисел. Первый INTEGER должен быть 0; если это не так, то ключ RSA имеет более двух основных факторов, и вы обречены. Восемь последующих INTEGER - это целые числа, которые вы ищете; просто расшифруйте их, и все готово. Последнее поле (otherPrimeInfos
) является необязательным и должно отсутствовать, если ваш ключ RSA является «нормальным» ключом RSA (с двумя простыми множителями, а не с тремя и более).