繁体   English   中英

C# RSA加解密带传输

[英]C# RSA encryption/decryption with transmission

我在 C# 的 .net 上看过很多使用 System.Security.Cryptography.RSACryptoServiceProvider 的加密/解密教程和示例,但我希望能够做的是:

  • 创建 RSA 公钥/私钥对
  • 传输公钥(或为了概念证明,只需将其移动到字符串变量中)
  • 创建一个新的 RSA 加密提供程序并使用公钥加密字符串
  • 将加密的字符串(或数据)传输回原始加密提供者并解密字符串

谁能指出我对此有用的资源?

好吧,确实有足够的例子,但无论如何,你去吧

using System;
using System.Security.Cryptography;

namespace RsaCryptoExample
{
  static class Program
  {
    static void Main()
    {
      //lets take a new CSP with a new 2048 bit rsa key pair
      var csp = new RSACryptoServiceProvider(2048);

      //how to get the private key
      var privKey = csp.ExportParameters(true);

      //and the public key ...
      var pubKey = csp.ExportParameters(false);

      //converting the public key into a string representation
      string pubKeyString;
      {
        //we need some buffer
        var sw = new System.IO.StringWriter();
        //we need a serializer
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
        //serialize the key into the stream
        xs.Serialize(sw, pubKey);
        //get the string from the stream
        pubKeyString = sw.ToString();
      }

      //converting it back
      {
        //get a stream from the string
        var sr = new System.IO.StringReader(pubKeyString);
        //we need a deserializer
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
        //get the object back from the stream
        pubKey = (RSAParameters)xs.Deserialize(sr);
      }

      //conversion for the private key is no black magic either ... omitted

      //we have a public key ... let's get a new csp and load that key
      csp = new RSACryptoServiceProvider();
      csp.ImportParameters(pubKey);

      //we need some data to encrypt
      var plainTextData = "foobar";

      //for encryption, always handle bytes...
      var bytesPlainTextData = System.Text.Encoding.Unicode.GetBytes(plainTextData);

      //apply pkcs#1.5 padding and encrypt our data 
      var bytesCypherText = csp.Encrypt(bytesPlainTextData, false);

      //we might want a string representation of our cypher text... base64 will do
      var cypherText = Convert.ToBase64String(bytesCypherText);


      /*
       * some transmission / storage / retrieval
       * 
       * and we want to decrypt our cypherText
       */

      //first, get our bytes back from the base64 string ...
      bytesCypherText = Convert.FromBase64String(cypherText);

      //we want to decrypt, therefore we need a csp and load our private key
      csp = new RSACryptoServiceProvider();
      csp.ImportParameters(privKey);

      //decrypt and strip pkcs#1.5 padding
      bytesPlainTextData = csp.Decrypt(bytesCypherText, false);

      //get our original plainText back...
      plainTextData = System.Text.Encoding.Unicode.GetString(bytesPlainTextData);
    }
  }
}

作为旁注:对 Encrypt() 和 Decrypt() 的调用有一个 bool 参数,可以在 OAEP 和 PKCS#1.5 填充之间切换……如果在您的情况下可用,您可能想要选择 OAEP

public static string Encryption(string strText)
        {
            var publicKey = "<RSAKeyValue><Modulus>21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

            var testData = Encoding.UTF8.GetBytes(strText);

            using (var rsa = new RSACryptoServiceProvider(1024))
            {
                try
                {
                    // client encrypting data with public key issued by server                    
                    rsa.FromXmlString(publicKey.ToString());

                    var encryptedData = rsa.Encrypt(testData, true);

                    var base64Encrypted = Convert.ToBase64String(encryptedData);

                    return base64Encrypted;
                }
                finally
                {
                    rsa.PersistKeyInCsp = false;
                }
            }
        }

        public static string Decryption(string strText)
        {
            var privateKey = "<RSAKeyValue><Modulus>21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=</Modulus><Exponent>AQAB</Exponent><P>/aULPE6jd5IkwtWXmReyMUhmI/nfwfkQSyl7tsg2PKdpcxk4mpPZUdEQhHQLvE84w2DhTyYkPHCtq/mMKE3MHw==</P><Q>3WV46X9Arg2l9cxb67KVlNVXyCqc/w+LWt/tbhLJvV2xCF/0rWKPsBJ9MC6cquaqNPxWWEav8RAVbmmGrJt51Q==</Q><DP>8TuZFgBMpBoQcGUoS2goB4st6aVq1FcG0hVgHhUI0GMAfYFNPmbDV3cY2IBt8Oj/uYJYhyhlaj5YTqmGTYbATQ==</DP><DQ>FIoVbZQgrAUYIHWVEYi/187zFd7eMct/Yi7kGBImJStMATrluDAspGkStCWe4zwDDmdam1XzfKnBUzz3AYxrAQ==</DQ><InverseQ>QPU3Tmt8nznSgYZ+5jUo9E0SfjiTu435ihANiHqqjasaUNvOHKumqzuBZ8NRtkUhS6dsOEb8A2ODvy7KswUxyA==</InverseQ><D>cgoRoAUpSVfHMdYXW9nA3dfX75dIamZnwPtFHq80ttagbIe4ToYYCcyUz5NElhiNQSESgS5uCgNWqWXt5PnPu4XmCXx6utco1UVH8HGLahzbAnSy6Cj3iUIQ7Gj+9gQ7PkC434HTtHazmxVgIR5l56ZjoQ8yGNCPZnsdYEmhJWk=</D></RSAKeyValue>";

            var testData = Encoding.UTF8.GetBytes(strText);

            using (var rsa = new RSACryptoServiceProvider(1024))
            {
                try
                {                    
                    var base64Encrypted = strText;

                    // server decrypting data with private key                    
                    rsa.FromXmlString(privateKey);

                    var resultBytes = Convert.FromBase64String(base64Encrypted);
                    var decryptedBytes = rsa.Decrypt(resultBytes, true);
                    var decryptedData = Encoding.UTF8.GetString(decryptedBytes);
                    return decryptedData.ToString();
                }
                finally
                {
                    rsa.PersistKeyInCsp = false;
                }
            }
        }

老实说,我很难实现它,因为我搜索过的几乎没有任何教程显示将密钥写入文件。 接受的答案是“很好”。 但对我来说,我必须改进它,以便将两个键都保存到两个单独的文件中。 我写了一个助手类,所以你们只需要复制并粘贴它。 希望这有助于大声笑。

using Microsoft.Win32;
using System;
using System.IO;
using System.Security.Cryptography;

namespace RsaCryptoExample
{
    class RSAFileHelper
    {
        readonly string pubKeyPath = "public.key";//change as needed
        readonly string priKeyPath = "private.key";//change as needed
        public void MakeKey()
        {
            //lets take a new CSP with a new 2048 bit rsa key pair
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider(2048);

            //how to get the private key
            RSAParameters privKey = csp.ExportParameters(true);

            //and the public key ...
            RSAParameters pubKey = csp.ExportParameters(false);
            //converting the public key into a string representation
            string pubKeyString;
            {
                //we need some buffer
                var sw = new StringWriter();
                //we need a serializer
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                //serialize the key into the stream
                xs.Serialize(sw, pubKey);
                //get the string from the stream
                pubKeyString = sw.ToString();
                File.WriteAllText(pubKeyPath, pubKeyString);
            }
            string privKeyString;
            {
                //we need some buffer
                var sw = new StringWriter();
                //we need a serializer
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                //serialize the key into the stream
                xs.Serialize(sw, privKey);
                //get the string from the stream
                privKeyString = sw.ToString();
                File.WriteAllText(priKeyPath, privKeyString);
            }
        }
        public void EncryptFile(string filePath)
        {
            //converting the public key into a string representation
            string pubKeyString;
            {
                using (StreamReader reader = new StreamReader(pubKeyPath)){pubKeyString = reader.ReadToEnd();}
            }
            //get a stream from the string
            var sr = new StringReader(pubKeyString);

            //we need a deserializer
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));

            //get the object back from the stream
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
            csp.ImportParameters((RSAParameters)xs.Deserialize(sr));
            byte[] bytesPlainTextData = File.ReadAllBytes(filePath);

            //apply pkcs#1.5 padding and encrypt our data 
            var bytesCipherText = csp.Encrypt(bytesPlainTextData, false);
            //we might want a string representation of our cypher text... base64 will do
            string encryptedText = Convert.ToBase64String(bytesCipherText);
            File.WriteAllText(filePath,encryptedText);
        }
        public void DecryptFile(string filePath)
        {
            //we want to decrypt, therefore we need a csp and load our private key
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();

            string privKeyString;
            {
                privKeyString = File.ReadAllText(priKeyPath);
                //get a stream from the string
                var sr = new StringReader(privKeyString);
                //we need a deserializer
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                //get the object back from the stream
                RSAParameters privKey = (RSAParameters)xs.Deserialize(sr);
                csp.ImportParameters(privKey);
            }
            string encryptedText;
            using (StreamReader reader = new StreamReader(filePath)) { encryptedText = reader.ReadToEnd(); }
            byte[] bytesCipherText = Convert.FromBase64String(encryptedText);

            //decrypt and strip pkcs#1.5 padding
            byte[] bytesPlainTextData = csp.Decrypt(bytesCipherText, false);

            //get our original plainText back...
            File.WriteAllBytes(filePath, bytesPlainTextData);
        }
    }
}

出于示例目的,我将分享我的非常简单的代码。 希望它能帮助像我这样搜索快速代码参考的人。 我的目标是从后端接收 rsa 签名,然后使用公钥验证输入字符串并在本地存储以备将来定期验证。 这是用于签名验证的主要部分:

        ...
        var signature = Get(url); // base64_encoded signature received from server
        var inputtext= "inputtext"; // this is main text signature was created for
        bool result = VerifySignature(inputtext, signature);
        ...

    private bool VerifySignature(string input, string signature)
    {
        var result = false;
        using (var cps=new RSACryptoServiceProvider())
        {
            // converting input and signature to Bytes Arrays to pass to VerifyData rsa method to verify inputtext was signed using privatekey corresponding to public key we have below
            byte[] inputtextBytes = Encoding.UTF8.GetBytes(input);
            byte[] signatureBytes  = Convert.FromBase64String(signature);

            cps.FromXmlString("<RSAKeyValue><Modulus>....</Modulus><Exponent>....</Exponent></RSAKeyValue>"); // xml formatted publickey
            result = cps.VerifyData(inputtextBytes , new SHA1CryptoServiceProvider(), signatureBytes  );
        }

        return result;
    }

对于大数据

public class RsaService : System.IDisposable
{
public delegate int TransformBlockCall(System.ReadOnlySpan<byte> data, System.Span<byte> destination);

private readonly RSA _encoder;
private readonly RSAEncryptionPadding _padding;

private readonly TransformBlockCall _encryptBlockCall;
private readonly TransformBlockCall _decryptBlockCall;

private int _encrypt_InputBlockSize;
private int _encrypt_OutputBlockSize;
private int _decrypt_InputBlockSize;
private int _decrypt_OutputBlockSize;

public RsaService(RSA encoder) {
    if(encoder == null)
        throw new System.ArgumentNullException(nameof(encoder));
    _encoder = encoder;

    _padding = RSAEncryptionPadding.Pkcs1;

    _encryptBlockCall = new TransformBlockCall(EncryptBlock);
    _decryptBlockCall = new TransformBlockCall(DecryptBlock);

    OnEndSetParameters();
}

private void OnEndSetParameters() {
    _encrypt_InputBlockSize = GetSizeOutputEncryptOfKeySize(_encoder.KeySize);
    _encrypt_OutputBlockSize = _encoder.KeySize / 8;
    _decrypt_InputBlockSize = _encrypt_OutputBlockSize;
    _decrypt_OutputBlockSize = _encrypt_OutputBlockSize;
}

public void ImportParameters(RSAParameters parameters) {
    _encoder.ImportParameters(parameters);
    OnEndSetParameters();
}

public byte[] Encrypt(byte[] data) {
    if(data == null) throw new System.ArgumentNullException(nameof(data));
    if(data.Length == 0) return data;
    int outputLength = GetEncryptOutputMaxByteCount(data.Length);
    byte[] outputData = new byte[outputLength];
    Encrypt(data, outputData);
    return outputData;
}


public byte[] Decrypt(byte[] data) {
    if(data == null) throw new System.ArgumentNullException(nameof(data));
    if(data.Length == 0) return data;
    int maxOutputLength = GetDecryptOutputMaxByteCount(data.Length);
    byte[] outputData = new byte[maxOutputLength];
    int actual_OutputLength = Decrypt(data, outputData);
    if(maxOutputLength > actual_OutputLength)
        System.Array.Resize(ref outputData, actual_OutputLength);
    return outputData;
}

public int Encrypt(System.ReadOnlySpan<byte> data, System.Span<byte> destination) {
#if DEBUG
    int inputBlockSize = _encrypt_InputBlockSize;
    int outputBlockSize = _encoder.KeySize / 8;
    int blockCount = (data.Length / inputBlockSize);
    if(data.Length % inputBlockSize != 0)
        blockCount++;
    System.Diagnostics.Debug.Assert((blockCount * outputBlockSize) <= destination.Length);
#endif

    if(data.Length > _encrypt_InputBlockSize)
        return TransformFinal(_encryptBlockCall, data, destination, _encrypt_InputBlockSize);
    else
        return _encryptBlockCall(data, destination);
}


public int Decrypt(System.ReadOnlySpan<byte> data, System.Span<byte> destination) {
    if(data.Length > _decrypt_InputBlockSize)
        return TransformFinal(_decryptBlockCall, data, destination, _decrypt_InputBlockSize);
    else
        return _decryptBlockCall(data, destination);
}

private int EncryptBlock(System.ReadOnlySpan<byte> data, System.Span<byte> destination) => _encoder.Encrypt(data, destination, _padding);
private int DecryptBlock(System.ReadOnlySpan<byte> data, System.Span<byte> destination) => _encoder.Decrypt(data, destination, _padding);

public int GetEncryptOutputMaxByteCount(int inputCount) => GetBlockCount(inputCount, _encrypt_InputBlockSize) * _encrypt_OutputBlockSize;
public int GetDecryptOutputMaxByteCount(int inputCount) => GetBlockCount(inputCount, _decrypt_InputBlockSize) * _decrypt_OutputBlockSize;
public void Dispose() {
    _encoder.Dispose();
    System.GC.SuppressFinalize(this);
}


#region Methods_Helper

public static RsaService Create(RSAParameters parameters) => new RsaService(RSA.Create(parameters));

public static RsaService Create() => new RsaService(RSA.Create());

// [keySize] ÷ 8 - [11 bytes for padding] = Result
// Exsimple: [2048 key size] ÷ 8 - [11 bytes for padding] = 245
public static int GetSizeOutputEncryptOfKeySize(int keySize) => (keySize / 8) - 11;

private static int GetBlockCount(int dataLength,int inputBlockSize) {
    int blockCount = (dataLength / inputBlockSize);
    if(dataLength % inputBlockSize != 0)
        blockCount++;
    return blockCount;
}

public static int TransformFinal(TransformBlockCall transformBlockCall, System.ReadOnlySpan<byte> data, System.Span<byte> destination, int inputBlockSize) {

    int blockCount = GetBlockCount(data.Length, inputBlockSize);

    int data_writtenCount = 0;
    int destination_writtenCount = 0;
    while(blockCount-- > 0) {
        if(blockCount == 0) {
            inputBlockSize = data.Length - data_writtenCount;
            if(inputBlockSize == 0) break;
        }
        destination_writtenCount += transformBlockCall(data: data.Slice(data_writtenCount, inputBlockSize)
            , destination: destination.Slice(destination_writtenCount));
        data_writtenCount += inputBlockSize;
    }
    return destination_writtenCount;
}


public static (RSAParameters keyPublic, RSAParameters keyPrivate) GenerateKeyPair(int keySize = 2048) {
    RSAParameters keyPriv;
    RSAParameters keyPub;
    using(var rsa = RSA.Create(keySize)) {
        keyPriv = rsa.ExportParameters(true);
        keyPub = rsa.ExportParameters(false);
    }
    return (keyPub, keyPriv);
}

#endregion Methods_Helper


}


public static class Program
{

static void Main() {

    var (keyPublic, keyPrivate) = RsaService.GenerateKeyPair();

    var encryptor = RsaService.Create(keyPublic);
    var decryptor = RsaService.Create(keyPrivate);
    string originalText = "";
    for(int i = 0; i < 1000; i++) {
        originalText += "ABC123456789";
    }
    byte[] inputData = Encoding.UTF8.GetBytes(originalText); // data random for test
    System.Console.WriteLine("inputData.Length: {0}", inputData.Length);

    var encryptedData = encryptor.Encrypt(inputData);

    System.Console.WriteLine("encryptedData.Length: {0}", encryptedData.Length);


    byte[] decryptedData = decryptor.Decrypt(encryptedData);
    string decryptedText = Encoding.UTF8.GetString(decryptedData);

    System.Console.WriteLine("status: {0}", decryptedText == originalText);

}
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM