如何仅在C#中获取RSACryptoServiceProvider公钥和私钥

ARH*_*ARH 3 c# asp.net rsa public-key-encryption

我正在下面的代码中运行,仅获取公钥和私钥,但似乎它输出了整个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();
        }
Run Code Online (Sandbox Code Playgroud)

bar*_*njs 7

从.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();
}
Run Code Online (Sandbox Code Playgroud)

因此,产生字符串:

string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");
Run Code Online (Sandbox Code Playgroud)

半手动

如果您不能使用.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");
}
Run Code Online (Sandbox Code Playgroud)

并制作私钥:

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");
}
Run Code Online (Sandbox Code Playgroud)

读回他们

.NET Core 3.0内置API

.NET Core 3.0不能理解PEM编码,因此您必须自己执行PEM-> binary:

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));
}
Run Code Online (Sandbox Code Playgroud)

完成后,您现在可以加载密钥:

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

    ...
}

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

半手动

如果您不能使用.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();
}
Run Code Online (Sandbox Code Playgroud)

而且由于私钥值必须具有正确的大小数组,所以私钥一个比较棘手:

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;
}
Run Code Online (Sandbox Code Playgroud)