Как получить открытый и закрытый ключ RSACryptoServiceProvider только в c # - PullRequest
0 голосов
/ 01 января 2019

Я использую код ниже, чтобы получить только открытый и закрытый ключ, но, похоже, он выводит весь формат XML.Мне нужно только вывести ключи, как показано в Демо с открытым и закрытым ключом

        static RSACryptoServiceProvider rsa;
        private RSAParameters _privateKey;
        private RSAParameters _publicKey;
        public RSACrypto()
        {
            rsa = new RSACryptoServiceProvider(2048);
            _privateKey = rsa.ExportParameters(true);
            _publicKey = rsa.ExportParameters(false);

        }
        public string GetPublicKeyString()
        {
            var sw = new StringWriter();
            var xs = new XmlSerializer(typeof(RSAParameters));
            xs.Serialize(sw, _publicKey);
            return sw.ToString();
        }
        public string GetPrivateKeyString()
        {
            var sw = new StringWriter();
            var xs = new XmlSerializer(typeof(RSAParameters));
            xs.Serialize(sw, _privateKey);
            return sw.ToString();
        }

Ответы [ 2 ]

0 голосов
/ 02 января 2019

В библиотеке Bouncycastle C # есть несколько вспомогательных классов, которые могут сделать это относительно легко.К сожалению, это не очень хорошо задокументировано.Вот пример:

using System;
using System.IO;
using System.Security.Cryptography;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;

namespace ExportToStandardFormats
{
    class MainClass
    {

        public static void Main(string[] args)
        {
            var rsa = new RSACryptoServiceProvider(2048);
            var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
            var writer = new StringWriter();
            var pemWriter = new PemWriter(writer);
            pemWriter.WriteObject(rsaKeyPair.Public);
            pemWriter.WriteObject(rsaKeyPair.Private);
            Console.WriteLine(writer);
        }
    }
}
0 голосов
/ 02 января 2019

Начиная с .NET Core 3.0, он (в основном) встроен.

Запись SubjectPublicKeyInfo и RSAPrivateKey

.NET Core 3.0 встроенный API

выходные данные встроенного API - это двоичное представление, для того чтобы сделать их PEM, вам необходимо вывести заголовок, нижний колонтитул и base64:

private static string MakePem(byte[] ber, string header)
{
    StringBuilder builder = new StringBuilder("-----BEGIN ");
    builder.Append(header);
    builder.AppendLine("-----");

    string base64 = Convert.ToBase64String(ber);
    int offset = 0;
    const int LineLength = 64;

    while (offset < base64.Length)
    {
        int lineEnd = Math.Min(offset + LineLength, base64.Length);
        builder.AppendLine(base64.Substring(offset, lineEnd - offset));
        offset = lineEnd;
    }

    builder.Append("-----END ");
    builder.Append(header);
    builder.AppendLine("-----");
    return builder.ToString();
}

Таким образом, чтобы получить строки:

string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");

Полу-вручную

Если вы не можете использовать .NET Core 3.0, но вы можете использовать предварительно выпущенные пакеты NuGet, вы можете использовать прототип прототип писателя ASN.1 (которыйэто тот же код, который используется внутри .NET Core 3.0, просто поверхность API не завершена).

Чтобы сделать открытый ключ:

private static string ToSubjectPublicKeyInfo(RSA rsa)
{
    RSAParameters rsaParameters = rsa.ExportParameters(false);

    AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
    writer.PushSequence();

    writer.PushSequence();
    writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
    writer.WriteNull();
    writer.PopSequence();

    AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);

    innerWriter.PushSequence();
    WriteRSAParameter(innerWriter, rsaParameters.Modulus);
    WriteRSAParameter(innerWriter, rsaParameters.Exponent);
    innerWriter.PopSequence();

    writer.WriteBitString(innerWriter.Encode());

    writer.PopSequence();
    return MakePem(writer.Encode(), "PUBLIC KEY");
}

И сделатьзакрытый ключ:

private static string ToRSAPrivateKey(RSA rsa)
{
    RSAParameters rsaParameters = rsa.ExportParameters(true);

    AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
    writer.PushSequence();

    writer.WriteInteger(0);
    WriteRSAParameter(writer, rsaParameters.Modulus);
    WriteRSAParameter(writer, rsaParameters.Exponent);
    WriteRSAParameter(writer, rsaParameters.D);
    WriteRSAParameter(writer, rsaParameters.P);
    WriteRSAParameter(writer, rsaParameters.Q);
    WriteRSAParameter(writer, rsaParameters.DP);
    WriteRSAParameter(writer, rsaParameters.DQ);
    WriteRSAParameter(writer, rsaParameters.InverseQ);

    writer.PopSequence();
    return MakePem(writer.Encode(), "RSA PRIVATE KEY");
}

Чтение их обратно

.NET Core 3.0 встроенный API

За исключением того, что .NET Core 3.0 не понимает PEM-кодировку, поэтому выВы должны выполнить PEM-> двоичный файл самостоятельно:

private const string RsaPrivateKey = "RSA PRIVATE KEY";
private const string SubjectPublicKeyInfo = "PUBLIC KEY";

private static byte[] PemToBer(string pem, string header)
{
    // Technically these should include a newline at the end,
    // and either newline-or-beginning-of-data at the beginning.
    string begin = $"-----BEGIN {header}-----";
    string end = $"-----END {header}-----";

    int beginIdx = pem.IndexOf(begin);
    int base64Start = beginIdx + begin.Length;
    int endIdx = pem.IndexOf(end, base64Start);

    return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
}

После того, как это сделано, вы можете загрузить ключи:

using (RSA rsa = RSA.Create())
{
    rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);

    ...
}

using (RSA rsa = RSA.Create())
{
    rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);

    ...
}

Полу-вручную

Если вы не можете использовать .NET Core 3.0, но вы можете использовать предварительно выпущенные пакеты NuGet, вы можете использовать прототип пакета считывателя ASN.1 (которыйэто тот же код, который используется внутри .NET Core 3.0;это просто, что поверхность API не завершена).

Для открытого ключа:

private static RSA FromSubjectPublicKeyInfo(string pem)
{
    AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
    AsnReader spki = reader.ReadSequence();
    reader.ThrowIfNotEmpty();

    AsnReader algorithmId = spki.ReadSequence();

    if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
    {
        throw new InvalidOperationException();
    }

    algorithmId.ReadNull();
    algorithmId.ThrowIfNotEmpty();

    AsnReader rsaPublicKey = spki.ReadSequence();

    RSAParameters rsaParameters = new RSAParameters
    {
        Modulus = ReadNormalizedInteger(rsaPublicKey),
        Exponent = ReadNormalizedInteger(rsaPublicKey),
    };

    rsaPublicKey.ThrowIfNotEmpty();

    RSA rsa = RSA.Create();
    rsa.ImportParameters(rsaParameters);
    return rsa;
}

private static byte[] ReadNormalizedInteger(AsnReader reader)
{
    ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
    ReadOnlySpan<byte> span = memory.Span;

    if (span[0] == 0)
    {
        span = span.Slice(1);
    }

    return span.ToArray();
}

И так как значения закрытого ключа должны иметь массивы правильного размера, закрытый ключ - одиннемного сложнее:

private static RSA FromRSAPrivateKey(string pem)
{
    AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
    AsnReader rsaPrivateKey = reader.ReadSequence();
    reader.ThrowIfNotEmpty();

    if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
    {
        throw new InvalidOperationException();
    }

    byte[] modulus = ReadNormalizedInteger(rsaPrivateKey);
    int halfModulusLen = (modulus.Length + 1) / 2;

    RSAParameters rsaParameters = new RSAParameters
    {
        Modulus = modulus,
        Exponent = ReadNormalizedInteger(rsaPrivateKey),
        D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
        P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
        Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
        DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
        DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
        InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
    };

    rsaPrivateKey.ThrowIfNotEmpty();

    RSA rsa = RSA.Create();
    rsa.ImportParameters(rsaParameters);
    return rsa;
}

private static byte[] ReadNormalizedInteger(AsnReader reader, int length)
{
    ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
    ReadOnlySpan<byte> span = memory.Span;

    if (span[0] == 0)
    {
        span = span.Slice(1);
    }

    byte[] buf = new byte[length];
    int skipSize = length - span.Length;
    span.CopyTo(buf.AsSpan(skipSize));
    return buf;
}
...