Use PEM encoded RSA private key in .NET

前端 未结 5 2025
囚心锁ツ
囚心锁ツ 2021-01-06 22:21

I have a private key which looks like this:

-----BEGIN RSA PRIVATE KEY----- Some private key data -----END RSA PRIVA

相关标签:
5条回答
  • 2021-01-06 22:37

    Though an older post I though I'd glue my own answer to this question as I bumped into the same challenge earlier this year. I wrote a library for the handling of PEM keys, encryption, decryption, signing and signature verification. There's an complete example solution attached to it (Load-Encrypt-Decrypt-Save), so you should be able to be up running in no-time.

    https://github.com/jrnker/CSharp-easy-RSA-PEM

    Cheers, Christoffer

    0 讨论(0)
  • 2021-01-06 22:41
    1. step 1 get "Some private key data" content.remove -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----, Removes all line symbols("\n");
    2. step 2. parse key to RSA.
    private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
    {
        var privateKeyBits = System.Convert.FromBase64String(privateKey);
    
        var RSA = new RSACryptoServiceProvider();
        var RSAparams = new RSAParameters();
    
        using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
        {
            byte bt = 0;
            ushort twobytes = 0;
            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130)
                binr.ReadByte();
            else if (twobytes == 0x8230)
                binr.ReadInt16();
            else
                throw new Exception("Unexpected value read binr.ReadUInt16()");
    
            twobytes = binr.ReadUInt16();
            if (twobytes != 0x0102)
                throw new Exception("Unexpected version");
    
            bt = binr.ReadByte();
            if (bt != 0x00)
                throw new Exception("Unexpected value read binr.ReadByte()");
    
            RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
        }
    
        RSA.ImportParameters(RSAparams);
        return RSA;
    }
    
    private int GetIntegerSize(BinaryReader binr)
    {
        byte bt = 0;
        byte lowbyte = 0x00;
        byte highbyte = 0x00;
        int count = 0;
        bt = binr.ReadByte();
        if (bt != 0x02)
            return 0;
        bt = binr.ReadByte();
    
        if (bt == 0x81)
            count = binr.ReadByte();
        else
            if (bt == 0x82)
            {
                highbyte = binr.ReadByte();
                lowbyte = binr.ReadByte();
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;
            }
    
        while (binr.ReadByte() == 0x00)
        {
            count -= 1;
        }
        binr.BaseStream.Seek(-1, SeekOrigin.Current);
        return count;
    }
    
    0 讨论(0)
  • 2021-01-06 22:46

    All major .NET/C# Cryptography libraries (like BouncyCastle, or SecureBlackbox [commercial]) should support this format, as well as operations with loaded key (encryption/decryption/signing/verification).

    0 讨论(0)
  • 2021-01-06 22:55

    The following article I wrote about digital signature gives a solution in c# (no additional library needed). The code shows how a RSA private key in PEM format can be converted to the XML format used by the .NET RSACryptoServiceProvider Class.

    With Atlando Crypto Currency Geo Service your identity is stored in your browser after registration. At each request a contract with us is signed and encrypted by this identity with your private key. This article explains how it works.

    Code below gives an implementation in C# (RSACryptoServiceProvider Class) of the authentication process through comparison of the original and a signed version. The modulus comes from the RSA public key in PEM format (exponent AQAB).

      private static bool Verify(string original, string signature, string modulus)
      {
        SHA256Managed sha = new SHA256Managed();
    
        byte[] bytes = Encoding.UTF8.GetBytes(original);
        byte[] hash = sha.ComputeHash(bytes);
    
        sha.Clear();
    
        byte[] signed = new byte[signature.Length/2];
    
        for (int i = 0; i < signature.Length; i += 2)
        {
          signed[i/2] = Convert.ToByte(Convert.ToInt32(signature.Substring(i, 2), 16));
        }
    
        string key = "<RSAKeyValue><Modulus>" + modulus + "</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
    
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
          rsa.FromXmlString(key);
    
          RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
    
          RSADeformatter.SetHashAlgorithm("SHA256");
    
          return RSADeformatter.VerifySignature(hash, signed);
        }
      }
    
    
      public static string Modulus(string pem)
      {
        byte[] x509der = Convert.FromBase64String(pem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", ""));
    
        byte[] seqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
    
        MemoryStream ms = new MemoryStream(x509der);
        BinaryReader reader = new BinaryReader(ms);
    
        if (reader.ReadByte() == 0x30) ReadASNLength(reader); //skip the size
        else return null;
    
        int identifierSize = 0; //total length of Object Identifier section
    
        if (reader.ReadByte() == 0x30) identifierSize = ReadASNLength(reader);
        else return null;
    
        if (reader.ReadByte() == 0x06) //is the next element an object identifier?
        {
          int oidLength = ReadASNLength(reader);
          byte[] oidBytes = new byte[oidLength];
          reader.Read(oidBytes, 0, oidBytes.Length);
    
          if (oidBytes.SequenceEqual(seqOID) == false) return null; //is the object identifier rsaEncryption PKCS#1?
    
          int remainingBytes = identifierSize - 2 - oidBytes.Length;
          reader.ReadBytes(remainingBytes);
        }
    
        if (reader.ReadByte() == 0x03) //is the next element a bit string?
        {
          ReadASNLength(reader); //skip the size
          reader.ReadByte(); //skip unused bits indicator
          if (reader.ReadByte() == 0x30)
          {
            ReadASNLength(reader); //skip the size
            if (reader.ReadByte() == 0x02) //is it an integer?
            {
              int modulusSize = ReadASNLength(reader);
              byte[] modulus = new byte[modulusSize];
              reader.Read(modulus, 0, modulus.Length);
              if (modulus[0] == 0x00) //strip off the first byte if it's 0
              {
                byte[] tempModulus = new byte[modulus.Length - 1];
                Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
                modulus = tempModulus;
              }
    
              if (reader.ReadByte() == 0x02) //is it an integer?
              {
                int exponentSize = ReadASNLength(reader);
                byte[] exponent = new byte[exponentSize];
                reader.Read(exponent, 0, exponent.Length);
    
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                RSAParameters RSAKeyInfo = new RSAParameters();
                RSAKeyInfo.Modulus = modulus;
                RSAKeyInfo.Exponent = exponent;
                rsa.ImportParameters(RSAKeyInfo);
                return rsa.ToXmlString(false).Replace("<RSAKeyValue><Modulus>", "").Replace("</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>", "");
              }
            }
          }
        }
    
        return null;
      }
    
    
      public static int ReadASNLength(BinaryReader reader)
      {//Note: this method only reads lengths up to 4 bytes long as this is satisfactory for the majority of situations.
        int length = reader.ReadByte();
        if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
        {
          int count = length & 0x0000000f;
          byte[] lengthBytes = new byte[4];
          reader.Read(lengthBytes, 4 - count, count);
          Array.Reverse(lengthBytes); //
          length = BitConverter.ToInt32(lengthBytes, 0);
        }
        return length;
      }
    
    0 讨论(0)
  • 2021-01-06 22:55

    First, you need to transform the private key to the form of RSA parameters using Bouncy Castle library. Then you need to pass the RSA parameters to the RSA algorithm as the private key. Lastly, you use the JWT library to encode and sign the token.

        public string GenerateJWTToken(string rsaPrivateKey)
        {
            var rsaParams = GetRsaParameters(rsaPrivateKey);
            var encoder = GetRS256JWTEncoder(rsaParams);
    
            // create the payload according to your need
            var payload = new Dictionary<string, object>
            {
                { "iss", ""},
                { "sub", "" },
                // and other key-values 
            };
    
            var token = encoder.Encode(payload, new byte[0]);
    
            return token;
        }
    
        private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
        {
            var csp = new RSACryptoServiceProvider();
            csp.ImportParameters(rsaParams);
    
            var algorithm = new RS256Algorithm(csp, csp);
            var serializer = new JsonNetSerializer();
            var urlEncoder = new JwtBase64UrlEncoder();
            var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
    
            return encoder;
        }
    
        private static RSAParameters GetRsaParameters(string rsaPrivateKey)
        {
            var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
            using (var ms = new MemoryStream(byteArray))
            {
                using (var sr = new StreamReader(ms))
                {
                    // use Bouncy Castle to convert the private key to RSA parameters
                    var pemReader = new PemReader(sr);
                    var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
                    return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
                }
            }
        }
    

    ps: the RSA private key should have the following format:

    -----BEGIN RSA PRIVATE KEY-----

    {base64 formatted value}

    -----END RSA PRIVATE KEY-----

    0 讨论(0)
提交回复
热议问题