PCLCrypto PEM PublicKey to Portable.BouncyCastle或System.Security.Cryptography .NetStandard2.0 Conversion

问题描述 投票:0回答:2

我不是来自安全背景,而是继承了以下代码。

我希望能够将其移植到.NET Standard中使用。

public sealed class Pem
{
    private string type;
    private string base64Encoded;

    private const string PemStart = "-----BEGIN ";
    private const string PemEnd = "-----END ";
    private const string PemEndOfLine = "-----";

    public Pem(string content)
    {
        using (var reader = new StringReader(content.Trim()))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                if (line.StartsWith(PemStart) && line.EndsWith(PemEndOfLine))
                {
                    type = line.Substring(PemStart.Length, line.Length - PemStart.Length - PemEndOfLine.Length);
                }

                else if (line.StartsWith(PemEnd))
                {
                    //ignore    
                }

                else
                {
                    base64Encoded += line;
                }
            }
        }
    }

    public string Type
    {
        get { return type; }
    }

    public byte[] Decoded
    {
        get { return Convert.FromBase64String(base64Encoded); }
    }
}

public class EncryptionService : IEncryption
{
    public string Encrypt(string target)
    {
        var key = "-----BEGIN PUBLIC KEY-----\nXXXXKEYMATERIALREMOVEDXXXX\n-----END PUBLIC KEY-----";

        var pem = new Pem(key);

        byte[] keyMaterial = pem.Decoded;
        byte[] data = Encoding.UTF8.GetBytes(target);

        var provider = PCLCrypto.WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm(PCLCrypto.AsymmetricAlgorithm.RsaPkcs1);
        var publicKey = provider.ImportPublicKey(keyMaterial);

        byte[] cipherText = PCLCrypto.WinRTCrypto.CryptographicEngine.Encrypt(publicKey, data);

        return Convert.ToBase64String(cipherText);
    }             
}
}

任何人都可以指出我正确的方向,我将如何使用其中任何一个重写.NET Standard 2.0

  • System.Security.Cryptography或
  • Portable.BouncyCastle?

假设密钥保持该字符串格式,如代码所示。

c# encryption cryptography .net-standard
2个回答
1
投票

将您的任务分为两部分:

  1. 将PEM文件导入X509Certificate2
  2. Use imported certificate with an encryption method

您可能想尝试下面的代码来完成第一阶段。

我正在使用它将PEM证书解码为X509Certificate2类。

public class Crypto
{
    /// <summary>
    /// This helper function parses an RSA private key using the ASN.1 format
    /// </summary>
    /// <param name="privateKeyBytes">Byte array containing PEM string of private key.</param>
    /// <returns>An instance of <see cref="RSACryptoServiceProvider"/> rapresenting the requested private key.
    /// Null if method fails on retriving the key.</returns>
    public static RSACryptoServiceProvider DecodeRsaPrivateKey(byte[] privateKeyBytes)
    {
        MemoryStream ms = new MemoryStream(privateKeyBytes);
        BinaryReader rd = new BinaryReader(ms);

        try
        {
            byte byteValue;
            ushort shortValue;

            shortValue = rd.ReadUInt16();

            switch (shortValue)
            {
                case 0x8130:
                    // If true, data is little endian since the proper logical seq is 0x30 0x81
                    rd.ReadByte(); //advance 1 byte
                    break;
                case 0x8230:
                    rd.ReadInt16();  //advance 2 bytes
                    break;
                default:
                    Debug.Assert(false);     // Improper ASN.1 format
                    return null;
            }

            shortValue = rd.ReadUInt16();
            if (shortValue != 0x0102) // (version number)
            {
                Debug.Assert(false);     // Improper ASN.1 format, unexpected version number
                return null;
            }

            byteValue = rd.ReadByte();
            if (byteValue != 0x00)
            {
                Debug.Assert(false);     // Improper ASN.1 format
                return null;
            }

            // The data following the version will be the ASN.1 data itself, which in our case
            // are a sequence of integers.

            // In order to solve a problem with instancing RSACryptoServiceProvider
            // via default constructor on .net 4.0 this is a hack
            CspParameters parms = new CspParameters();
            parms.Flags = CspProviderFlags.NoFlags;
            parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant();
            parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1;

            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms);
            RSAParameters rsAparams = new RSAParameters();

            rsAparams.Modulus = rd.ReadBytes(Helpers.DecodeIntegerSize(rd));

            // Argh, this is a pain.  From emperical testing it appears to be that RSAParameters doesn't like byte buffers that
            // have their leading zeros removed.  The RFC doesn't address this area that I can see, so it's hard to say that this
            // is a bug, but it sure would be helpful if it allowed that. So, there's some extra code here that knows what the
            // sizes of the various components are supposed to be.  Using these sizes we can ensure the buffer sizes are exactly
            // what the RSAParameters expect.  Thanks, Microsoft.
            RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8);

            rsAparams.Modulus = Helpers.AlignBytes(rsAparams.Modulus, traits.size_Mod);
            rsAparams.Exponent = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Exp);
            rsAparams.D = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_D);
            rsAparams.P = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_P);
            rsAparams.Q = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Q);
            rsAparams.DP = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DP);
            rsAparams.DQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DQ);
            rsAparams.InverseQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_InvQ);

            rsa.ImportParameters(rsAparams);
            return rsa;
        }
        catch (Exception)
        {
            Debug.Assert(false);
            return null;
        }
        finally
        {
            rd.Close();
        }
    }
}
   public class Helpers
{
    /// <summary>
    /// This helper function parses an integer size from the reader using the ASN.1 format
    /// </summary>
    /// <param name="rd"></param>
    /// <returns></returns>
    public static int DecodeIntegerSize(System.IO.BinaryReader rd)
    {
        byte byteValue;
        int count;

        byteValue = rd.ReadByte();
        if (byteValue != 0x02)        // indicates an ASN.1 integer value follows
            return 0;

        byteValue = rd.ReadByte();
        if (byteValue == 0x81)
        {
            count = rd.ReadByte();    // data size is the following byte
        }
        else if (byteValue == 0x82)
        {
            byte hi = rd.ReadByte();  // data size in next 2 bytes
            byte lo = rd.ReadByte();
            count = BitConverter.ToUInt16(new[] { lo, hi }, 0);
        }
        else
        {
            count = byteValue;        // we already have the data size
        }

        //remove high order zeros in data
        while (rd.ReadByte() == 0x00)
        {
            count -= 1;
        }
        rd.BaseStream.Seek(-1, System.IO.SeekOrigin.Current);

        return count;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="pemString"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public static byte[] GetBytesFromPEM(string pemString, PemStringType type)
    {
        string header; string footer;

        switch (type)
        {
            case PemStringType.Certificate:
                header = "-----BEGIN CERTIFICATE-----";
                footer = "-----END CERTIFICATE-----";
                break;
            case PemStringType.RsaPrivateKey:
                header = "-----BEGIN RSA PRIVATE KEY-----";
                footer = "-----END RSA PRIVATE KEY-----";
                break;
            default:
                return null;
        }

        int start = pemString.IndexOf(header) + header.Length;
        int end = pemString.IndexOf(footer, start) - start;
        return Convert.FromBase64String(pemString.Substring(start, end));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="inputBytes"></param>
    /// <param name="alignSize"></param>
    /// <returns></returns>
    public static byte[] AlignBytes(byte[] inputBytes, int alignSize)
    {
        int inputBytesSize = inputBytes.Length;

        if ((alignSize != -1) && (inputBytesSize < alignSize))
        {
            byte[] buf = new byte[alignSize];
            for (int i = 0; i < inputBytesSize; ++i)
            {
                buf[i + (alignSize - inputBytesSize)] = inputBytes[i];
            }
            return buf;
        }
        else
        {
            return inputBytes;      // Already aligned, or doesn't need alignment
        }
    }
}
    public enum PemStringType
{
    Certificate,
    RsaPrivateKey
}
    internal class RSAParameterTraits
{
    public RSAParameterTraits(int modulusLengthInBits)
    {
        // The modulus length is supposed to be one of the common lengths, which is the commonly referred to strength of the key,
        // like 1024 bit, 2048 bit, etc.  It might be a few bits off though, since if the modulus has leading zeros it could show
        // up as 1016 bits or something like that.
        int assumedLength = -1;
        double logbase = Math.Log(modulusLengthInBits, 2);
        if (logbase == (int)logbase)
        {
            // It's already an even power of 2
            assumedLength = modulusLengthInBits;
        }
        else
        {
            // It's not an even power of 2, so round it up to the nearest power of 2.
            assumedLength = (int)(logbase + 1.0);
            assumedLength = (int)(Math.Pow(2, assumedLength));
            System.Diagnostics.Debug.Assert(false);  // Can this really happen in the field?  I've never seen it, so if it happens
            // you should verify that this really does the 'right' thing!
        }

        switch (assumedLength)
        {
            case 1024:
                this.size_Mod = 0x80;
                this.size_Exp = -1;
                this.size_D = 0x80;
                this.size_P = 0x40;
                this.size_Q = 0x40;
                this.size_DP = 0x40;
                this.size_DQ = 0x40;
                this.size_InvQ = 0x40;
                break;
            case 2048:
                this.size_Mod = 0x100;
                this.size_Exp = -1;
                this.size_D = 0x100;
                this.size_P = 0x80;
                this.size_Q = 0x80;
                this.size_DP = 0x80;
                this.size_DQ = 0x80;
                this.size_InvQ = 0x80;
                break;
            case 4096:
                this.size_Mod = 0x200;
                this.size_Exp = -1;
                this.size_D = 0x200;
                this.size_P = 0x100;
                this.size_Q = 0x100;
                this.size_DP = 0x100;
                this.size_DQ = 0x100;
                this.size_InvQ = 0x100;
                break;
            default:
                System.Diagnostics.Debug.Assert(false); // Unknown key size?
                break;
        }
    }

    public int size_Mod = -1;
    public int size_Exp = -1;
    public int size_D = -1;
    public int size_P = -1;
    public int size_Q = -1;
    public int size_DP = -1;
    public int size_DQ = -1;
    public int size_InvQ = -1;
}

最后,您需要从您的程序中调用它:

       public X509Certificate2 GetCertificateFromText(string pem, string key)
    {
        byte[] certBuffer, keyBuffer;
        if (string.IsNullOrEmpty(pem))
        {
            throw new ArgumentNullException("Missing certificate");
        }
        try
        {
            certBuffer = Helpers.GetBytesFromPEM(pem, PemStringType.Certificate);
            keyBuffer = Helpers.GetBytesFromPEM(pem, PemStringType.RsaPrivateKey);
            X509Certificate2 certificate = new X509Certificate2(certBuffer, "your_key_password");

            RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
            certificate.PrivateKey = prov;

            return certificate;
        }
        catch (Exception ex)
        {
            throw new ArgumentException("Invalid certificate", ex);
        }

    }

我在csharpcorner上找到了代码,但是现在找不到原始链接。


0
投票

对不起,我们没有机会查看@komsky发布的上述代码,但是为了参考我们最后使用了这个答案所引用的PemKeyUtils类 -

How to load the RSA public key from file in C#

如下 -

public class EncryptionService : Interfaces.IEncryption
{
    public string Encrypt(string target)
    {
        var key = "-----BEGIN PUBLIC KEY-----\YOURKEY\n-----END PUBLIC KEY-----";

        var rsaProvider = PemKeyUtils.GetRSAProviderFromPemString(key);
        RSAEncyptionManager manager = new RSAEncyptionManager(rsaProvider, RSAEncryptionPadding.Pkcs1);         

        return Convert.ToBase64String(manager.Encrypt(target));
    }
}

public class RSAEncyptionManager {      
    private readonly RSACryptoServiceProvider _serviceProvider;
    private readonly RSAEncryptionPadding _padding;
    private RSAParameters _rSAKeyInfo;

    public RSAEncyptionManager(RSACryptoServiceProvider serviceProvider, RSAEncryptionPadding padding)
    {           
        _serviceProvider = serviceProvider;
        _padding = padding;

        _rSAKeyInfo = _serviceProvider.ExportParameters(false);
    }

    public byte[] Encrypt(string stringToEncrypt)
    {                                                                                   
        return _serviceProvider.Encrypt(Encoding.UTF8.GetBytes(stringToEncrypt), _padding);
    }

    public RSAEncyptionManager SetKey(byte[] key) {
        _rSAKeyInfo.Modulus = key;
        _serviceProvider.ImportParameters(_rSAKeyInfo);
        return this ;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.