cxi RSA非対称加算復号およびXML&PEMフォーマット互換案


最近はインタフェースの安全問題を考慮して、WEB APIに統一的なパラメータ認証機能を実現して、要求パラメータが改竄されたり、繰り返し実行されることを防止します。パラメータ認証方法は基本的に一般的な鑑権の考え方と同じです。timestamp+signを採用します。フォーマットした後にスペルをして更にMD 5)も先端で安全ではないため、timestampに対して非対称的に復号して、できるだけ生成のsignが解読されにくいことを保証します。
RSAを復号します。すなわち、非対称的に復号します。
公開鍵、秘密鍵対方法(C啣)を生成し、生成後のデフォルトはXML形式です。

    public static Tuple<string, string> GeneratePublicAndPrivateKeyPair()
    {
      using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
      {
        string publicKey = rsa.ToXmlString(false); //   
        string privateKey = rsa.ToXmlString(true); //   

        return Tuple.Create(publicKey, privateKey);
      }
    }
公開鍵を使って暗号化します。

public static string RSAEncrypt(string publicKey, string rawInput)
    {
      if (string.IsNullOrEmpty(rawInput))
      {
        return string.Empty;
      }

      if (string.IsNullOrWhiteSpace(publicKey))
      {
        throw new ArgumentException("Invalid Public Key");
      }

      using (var rsaProvider = new RSACryptoServiceProvider())
      {
        var inputBytes = Encoding.UTF8.GetBytes(rawInput);//             
        rsaProvider.FromXmlString(publicKey);//    
        int bufferSize = (rsaProvider.KeySize / 8) - 11;//      
        var buffer = new byte[bufferSize];
        using (MemoryStream inputStream = new MemoryStream(inputBytes),
           outputStream = new MemoryStream())
        {
          while (true)
          { //    
            int readSize = inputStream.Read(buffer, 0, bufferSize);
            if (readSize <= 0)
            {
              break;
            }

            var temp = new byte[readSize];
            Array.Copy(buffer, 0, temp, 0, readSize);
            var encryptedBytes = rsaProvider.Encrypt(temp, false);
            outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
          }
          return Convert.ToBase64String(outputStream.ToArray());//          
        }
      }
    }
秘密鍵を使って復号します。

 public static string RSADecrypt(string privateKey,string encryptedInput)
    {
      if (string.IsNullOrEmpty(encryptedInput))
      {
        return string.Empty;
      }

      if (string.IsNullOrWhiteSpace(privateKey))
      {
        throw new ArgumentException("Invalid Private Key");
      }

      using (var rsaProvider = new RSACryptoServiceProvider())
      {
        var inputBytes = Convert.FromBase64String(encryptedInput);
        rsaProvider.FromXmlString(privateKey);
        int bufferSize = rsaProvider.KeySize / 8;
        var buffer = new byte[bufferSize];
        using (MemoryStream inputStream = new MemoryStream(inputBytes),
           outputStream = new MemoryStream())
        {
          while (true)
          {
            int readSize = inputStream.Read(buffer, 0, bufferSize);
            if (readSize <= 0)
            {
              break;
            }

            var temp = new byte[readSize];
            Array.Copy(buffer, 0, temp, 0, readSize);
            var rawBytes = rsaProvider.Decrypt(temp, false);
            outputStream.Write(rawBytes, 0, rawBytes.Length);
          }
          return Encoding.UTF8.GetString(outputStream.ToArray());
        }
      }
    }
いずれもC(zhi)プロジェクトであれば上のようにしても良いですが、WEBフロントエンドやJAVAなどの他のプログラミング言語と連携して処理する必要がある場合(例えば、WEBのフロントエンドを公開鍵で暗号化し、バックエンドCキーを復号する)は、公開鍵と秘密鍵のフォーマットが異なるため、正常にドッキングできない可能性があります。これはXML形式を使用しています。インターネットでXMLからPEM形式に変換する場合、コピーしたものです。https://www.cnblogs.com/micenote/p/7862989.htmlこの文章ですが、実はこの文章も秘密鍵XMLからPEM形式に移行しただけです。公開鍵XMLがどのようにPEM形式に移行するかは説明されていません。しかもサポートを書いただけで、ファイルから内容を取得してから内容を再変換します。最終的には比較的友好的なPEMとXML形式の相互変換方式を実現しました。また、ユニットテストに合格しました。ここで皆さんに共有します。
下記は完全なXMLとPEMフォーマット変換器類コードです。注意BouncyCastle nugetバッグを導入する必要があります。)

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Zuowj.Common
{
  /// <summary>
  /// RSA  、     (XML PEM)   
  /// author:zuowenjun
  /// date:2020-12-29
  /// </summary>
  public static class RsaKeysFormatConverter
  {
    /// <summary>
    /// XML    Pem  
    /// </summary>
    /// <param name="xmlPublicKey"></param>
    /// <returns></returns>
    public static string XmlPublicKeyToPem(string xmlPublicKey)
    {
      RSAParameters rsaParam;
      using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
      {
        rsa.FromXmlString(xmlPublicKey);
        rsaParam = rsa.ExportParameters(false);
      }
      RsaKeyParameters param = new RsaKeyParameters(false, new BigInteger(1, rsaParam.Modulus), new BigInteger(1, rsaParam.Exponent));

      string pemPublicKeyStr = null;
      using (var ms = new MemoryStream())
      {
        using (var sw = new StreamWriter(ms))
        {
          var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
          pemWriter.WriteObject(param);
          sw.Flush();

          byte[] buffer = new byte[ms.Length];
          ms.Position = 0;
          ms.Read(buffer, 0, (int)ms.Length);
          pemPublicKeyStr = Encoding.UTF8.GetString(buffer);
        }
      }

      return pemPublicKeyStr;
    }

    /// <summary>
    /// Pem    XML  
    /// </summary>
    /// <param name="pemPublicKeyStr"></param>
    /// <returns></returns>
    public static string PemPublicKeyToXml(string pemPublicKeyStr)
    {
      RsaKeyParameters pemPublicKey;
      using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(pemPublicKeyStr)))
      {
        using (var sr = new StreamReader(ms))
        {
          var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr);
          pemPublicKey = (RsaKeyParameters)pemReader.ReadObject();
        }
      }

      var p = new RSAParameters
      {
        Modulus = pemPublicKey.Modulus.ToByteArrayUnsigned(),
        Exponent = pemPublicKey.Exponent.ToByteArrayUnsigned()
      };

      string xmlPublicKeyStr;
      using (var rsa = new RSACryptoServiceProvider())
      {
        rsa.ImportParameters(p);
        xmlPublicKeyStr = rsa.ToXmlString(false);
      }

      return xmlPublicKeyStr;
    }

    /// <summary>
    /// XML    PEM  
    /// </summary>
    /// <param name="xmlPrivateKey"></param>
    /// <returns></returns>
    public static string XmlPrivateKeyToPem(string xmlPrivateKey)
    {
      RSAParameters rsaParam;
      using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
      {
        rsa.FromXmlString(xmlPrivateKey);
        rsaParam = rsa.ExportParameters(true);
      }

      var param = new RsaPrivateCrtKeyParameters(
        new BigInteger(1, rsaParam.Modulus), new BigInteger(1, rsaParam.Exponent), new BigInteger(1, rsaParam.D),
        new BigInteger(1, rsaParam.P), new BigInteger(1, rsaParam.Q), new BigInteger(1, rsaParam.DP), new BigInteger(1, rsaParam.DQ),
        new BigInteger(1, rsaParam.InverseQ));

      string pemPrivateKeyStr = null;
      using (var ms = new MemoryStream())
      {
        using (var sw = new StreamWriter(ms))
        {
          var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
          pemWriter.WriteObject(param);
          sw.Flush();

          byte[] buffer = new byte[ms.Length];
          ms.Position = 0;
          ms.Read(buffer, 0, (int)ms.Length);
          pemPrivateKeyStr = Encoding.UTF8.GetString(buffer);
        }
      }

      return pemPrivateKeyStr;
    }

    /// <summary>
    /// Pem    XML  
    /// </summary>
    /// <param name="pemPrivateKeyStr"></param>
    /// <returns></returns>
    public static string PemPrivateKeyToXml(string pemPrivateKeyStr)
    {
      RsaPrivateCrtKeyParameters pemPrivateKey;
      using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(pemPrivateKeyStr)))
      {
        using (var sr = new StreamReader(ms))
        {
          var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr);
          var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
          pemPrivateKey = (RsaPrivateCrtKeyParameters)keyPair.Private;
        }
      }

      var p = new RSAParameters
      {
        Modulus = pemPrivateKey.Modulus.ToByteArrayUnsigned(),
        Exponent = pemPrivateKey.PublicExponent.ToByteArrayUnsigned(),
        D = pemPrivateKey.Exponent.ToByteArrayUnsigned(),
        P = pemPrivateKey.P.ToByteArrayUnsigned(),
        Q = pemPrivateKey.Q.ToByteArrayUnsigned(),
        DP = pemPrivateKey.DP.ToByteArrayUnsigned(),
        DQ = pemPrivateKey.DQ.ToByteArrayUnsigned(),
        InverseQ = pemPrivateKey.QInv.ToByteArrayUnsigned(),
      };

      string xmlPrivateKeyStr;
      using (var rsa = new RSACryptoServiceProvider())
      {
        rsa.ImportParameters(p);
        xmlPrivateKeyStr = rsa.ToXmlString(true);
      }

      return xmlPrivateKeyStr;
    }

  }
}
以下はユニットテストコードです。

//  (XML、PEM   )  
string srcPublicKey = “   XML Public Key”;
      string pemPublicKeyStr= RsaKeysFormatConverter.XmlPublicKeyToPem(publicKey);
      string xmlPublicKeyStr= RsaKeysFormatConverter.PemPublicKeyToXml(pemPublicKeyStr);
      Assert.AreEqual(srcPublicKey, xmlPublicKeyStr);

//  (XML、PEM   )  
string srcPrivateKey = “   XML Private Key”;
      string pemPrivateKeyStr = RsaKeysFormatConverter.XmlPrivateKeyToPem(srcPrivateKey);
      string xmlPrivateKeyStr = RsaKeysFormatConverter.PemPrivateKeyToXml(pemPrivateKeyStr);
      Assert.AreEqual(privateKey,xmlPrivateKeyStr)
もちろん、このように苦労しなくても大丈夫です。オンラインサイトを使って直接変換できます。また、同じような機能を実現した文章もあります。しかし、生成したPEMフォーマットは完全なフォーマットではなく、注釈の先頭が欠けています。https://the-x.cn/certificate/XmlToPem.aspx
以上はc㌦RSA非対称性のある解読及びXML&PEMフォーマット互換方案の詳細です。c130; RSA非対称性について解読する資料は他の関連記事に注目してください。