如何在C#中使用公钥和私钥加密技术

本文关键字:公钥 私钥 加密 技术 | 更新日期: 2023-09-27 18:29:14

我想使用公钥/私钥技术加密数据。我的意思是,用接收方的公钥加密,接收方可以用自己的私钥解密。

我该怎么做?你有什么建议或示例代码吗?

如何在C#中使用公钥和私钥加密技术

代码示例:

private static string _privateKey;
private static string _publicKey;
private static UnicodeEncoding _encoder = new UnicodeEncoding();
private static void RSA()
{
  var rsa = new RSACryptoServiceProvider();
  _privateKey = rsa.ToXmlString(true);
  _publicKey = rsa.ToXmlString(false);
  var text = "Test1";
  Console.WriteLine("RSA // Text to encrypt: " + text);
  var enc = Encrypt(text);
  Console.WriteLine("RSA // Encrypted Text: " + enc);
  var dec = Decrypt(enc);
  Console.WriteLine("RSA // Decrypted Text: " + dec);
}
public static string Decrypt(string data)
{
  var rsa = new RSACryptoServiceProvider();
  var dataArray = data.Split(new char[] { ',' });
  byte[] dataByte = new byte[dataArray.Length];
  for (int i = 0; i < dataArray.Length; i++)
  {
    dataByte[i] = Convert.ToByte(dataArray[i]);
  }
  rsa.FromXmlString(_privateKey);
  var decryptedByte = rsa.Decrypt(dataByte, false);
  return _encoder.GetString(decryptedByte);
}
public static string Encrypt(string data)
{
  var rsa = new RSACryptoServiceProvider();
  rsa.FromXmlString(_publicKey);
  var dataToEncrypt = _encoder.GetBytes(data);
  var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();
  var length = encryptedByteArray.Count();
  var item = 0;
  var sb = new StringBuilder();
  foreach (var x in encryptedByteArray)
  {
    item++;
    sb.Append(x);
    if (item < length)
      sb.Append(",");
  }
  return sb.ToString();
}

此示例:https://learn.microsoft.com/en-us/dotnet/standard/security/walkthrough-creating-a-cryptographic-application

#NKCSS发现的非常好。我用它构建了一个测试应用程序,通过了安全官员的代码审查。

只需复制示例中的相关部分,以防链接发生变化:

// Declare global objects
//
// Declare CspParmeters and RsaCryptoServiceProvider
// objects with global scope of your Form class.
readonly CspParameters _cspp = new CspParameters();
RSACryptoServiceProvider _rsa;
// Path variables for source, encryption, and
// decryption folders. Must end with a backslash.
const string EncrFolder = @"c:'Encrypt'";
const string DecrFolder = @"c:'Decrypt'";
const string SrcFolder = @"c:'docs'";
// Public key file
const string PubKeyFile = @"c:'encrypt'rsaPublicKey.txt";
// Key container name for
// private/public key value pair.
const string KeyName = "Key01";
private void buttonCreateAsmKeys_Click(object sender, EventArgs e)
{
    // Stores a key pair in the key container.
    _cspp.KeyContainerName = KeyName;
    _rsa = new RSACryptoServiceProvider(_cspp)
    {
        PersistKeyInCsp = true
    };
    label1.Text = _rsa.PublicOnly
        ? $"Key: {_cspp.KeyContainerName} - Public Only"
        : $"Key: {_cspp.KeyContainerName} - Full Key Pair";
}
private void buttonEncryptFile_Click(object sender, EventArgs e)
{
    if (_rsa is null)
    {
        MessageBox.Show("Key not set.");
    }
    else
    {
        // Display a dialog box to select a file to encrypt.
        _encryptOpenFileDialog.InitialDirectory = SrcFolder;
        if (_encryptOpenFileDialog.ShowDialog() == DialogResult.OK)
        {
            string fName = _encryptOpenFileDialog.FileName;
            if (fName != null)
            {
                // Pass the file name without the path.
                EncryptFile(new FileInfo(fName));
            }
        }
    }
}
// Add the following EncryptFile method to the form.
private void EncryptFile(FileInfo file)
{
    // Create instance of Aes for
    // symmetric encryption of the data.
    Aes aes = Aes.Create();
    ICryptoTransform transform = aes.CreateEncryptor();
    // Use RSACryptoServiceProvider to
    // encrypt the AES key.
    // rsa is previously instantiated:
    //    rsa = new RSACryptoServiceProvider(cspp);
    byte[] keyEncrypted = _rsa.Encrypt(aes.Key, false);
    // Create byte arrays to contain
    // the length values of the key and IV.
    int lKey = keyEncrypted.Length;
    byte[] LenK = BitConverter.GetBytes(lKey);
    int lIV = aes.IV.Length;
    byte[] LenIV = BitConverter.GetBytes(lIV);
    // Write the following to the FileStream
    // for the encrypted file (outFs):
    // - length of the key
    // - length of the IV
    // - ecrypted key
    // - the IV
    // - the encrypted cipher content
    // Change the file's extension to ".enc"
    string outFile = 
        Path.Combine(EncrFolder, Path.ChangeExtension(file.Name, ".enc"));
    using (var outFs = new FileStream(outFile, FileMode.Create))
    {
        outFs.Write(LenK, 0, 4);
        outFs.Write(LenIV, 0, 4);
        outFs.Write(keyEncrypted, 0, lKey);
        outFs.Write(aes.IV, 0, lIV);
        // Now write the cipher text using
        // a CryptoStream for encrypting.
        using (var outStreamEncrypted = 
            new CryptoStream(outFs, transform, CryptoStreamMode.Write))
        {
            // By encrypting a chunk at
            // a time, you can save memory
            // and accommodate large files.
            int count = 0;
            int offset = 0;
            // blockSizeBytes can be any arbitrary size.
            int blockSizeBytes = aes.BlockSize / 8;
            byte[] data = new byte[blockSizeBytes];
            int bytesRead = 0;
            using (var inFs = new FileStream(file.FullName, FileMode.Open))
            {
                do
                {
                    count = inFs.Read(data, 0, blockSizeBytes);
                    offset += count;
                    outStreamEncrypted.Write(data, 0, count);
                    bytesRead += blockSizeBytes;
                } while (count > 0);
            }
            outStreamEncrypted.FlushFinalBlock();
        }
    }
}
// Then to Decrypt a file -
private void buttonDecryptFile_Click(object sender, EventArgs e)
{
    if (_rsa is null)
    {
        MessageBox.Show("Key not set.");
    }
    else
    {
        // Display a dialog box to select the encrypted file.
        _decryptOpeFileDialog.InitialDirectory = EncrFolder;
        if (_decryptOpeFileDialog.ShowDialog() == DialogResult.OK)
        {
            string fName = _decryptOpeFileDialog.FileName;
            if (fName != null)
            {
                DecryptFile(new FileInfo(fName));
            }
        }
    }
}
// And -
private void DecryptFile(FileInfo file)
{
    // Create instance of Aes for
    // symmetric decryption of the data.
    Aes aes = Aes.Create();
    // Create byte arrays to get the length of
    // the encrypted key and IV.
    // These values were stored as 4 bytes each
    // at the beginning of the encrypted package.
    byte[] LenK = new byte[4];
    byte[] LenIV = new byte[4];
    // Construct the file name for the decrypted file.
    string outFile =
        Path.ChangeExtension(file.FullName.Replace("Encrypt", "Decrypt"), ".txt");
    // Use FileStream objects to read the encrypted
    // file (inFs) and save the decrypted file (outFs).
    using (var inFs = new FileStream(file.FullName, FileMode.Open))
    {
        inFs.Seek(0, SeekOrigin.Begin);
        inFs.Read(LenK, 0, 3);
        inFs.Seek(4, SeekOrigin.Begin);
        inFs.Read(LenIV, 0, 3);
        // Convert the lengths to integer values.
        int lenK = BitConverter.ToInt32(LenK, 0);
        int lenIV = BitConverter.ToInt32(LenIV, 0);
        // Determine the start postition of
        // the ciphter text (startC)
        // and its length(lenC).
        int startC = lenK + lenIV + 8;
        int lenC = (int)inFs.Length - startC;
        // Create the byte arrays for
        // the encrypted Aes key,
        // the IV, and the cipher text.
        byte[] KeyEncrypted = new byte[lenK];
        byte[] IV = new byte[lenIV];
        // Extract the key and IV
        // starting from index 8
        // after the length values.
        inFs.Seek(8, SeekOrigin.Begin);
        inFs.Read(KeyEncrypted, 0, lenK);
        inFs.Seek(8 + lenK, SeekOrigin.Begin);
        inFs.Read(IV, 0, lenIV);
        Directory.CreateDirectory(DecrFolder);
        // Use RSACryptoServiceProvider
        // to decrypt the AES key.
        byte[] KeyDecrypted = _rsa.Decrypt(KeyEncrypted, false);
        // Decrypt the key.
        ICryptoTransform transform = aes.CreateDecryptor(KeyDecrypted, IV);
        // Decrypt the cipher text from
        // from the FileSteam of the encrypted
        // file (inFs) into the FileStream
        // for the decrypted file (outFs).
        using (var outFs = new FileStream(outFile, FileMode.Create))
        {
            int count = 0;
            int offset = 0;
            // blockSizeBytes can be any arbitrary size.
            int blockSizeBytes = aes.BlockSize / 8;
            byte[] data = new byte[blockSizeBytes];
            // By decrypting a chunk a time,
            // you can save memory and
            // accommodate large files.
            // Start at the beginning
            // of the cipher text.
            inFs.Seek(startC, SeekOrigin.Begin);
            using (var outStreamDecrypted =
                new CryptoStream(outFs, transform, CryptoStreamMode.Write))
            {
                do
                {
                    count = inFs.Read(data, 0, blockSizeBytes);
                    offset += count;
                    outStreamDecrypted.Write(data, 0, count);
                } while (count > 0);
                outStreamDecrypted.FlushFinalBlock();
            }
        }
    }
}
// you can also try to Export a public key:
void buttonExportPublicKey_Click(object sender, EventArgs e)
{
    // Save the public key created by the RSA
    // to a file. Caution, persisting the
    // key to a file is a security risk.
    Directory.CreateDirectory(EncrFolder);
    using (var sw = new StreamWriter(PubKeyFile, false))
    {
        sw.Write(_rsa.ToXmlString(false));
    }
}
// or Import a public key
void buttonImportPublicKey_Click(object sender, EventArgs e)
{
    using (var sr = new StreamReader(PubKeyFile))
    {
        _cspp.KeyContainerName = KeyName;
        _rsa = new RSACryptoServiceProvider(_cspp);
        string keytxt = sr.ReadToEnd();
        _rsa.FromXmlString(keytxt);
        _rsa.PersistKeyInCsp = true;
        label1.Text = _rsa.PublicOnly
            ? $"Key: {_cspp.KeyContainerName} - Public Only"
            : $"Key: {_cspp.KeyContainerName} - Full Key Pair";
    }
}
// (Get a private key)
private void buttonGetPrivateKey_Click(object sender, EventArgs e)
{
    _cspp.KeyContainerName = KeyName;
    _rsa = new RSACryptoServiceProvider(_cspp)
    {
        PersistKeyInCsp = true
    };
    label1.Text = _rsa.PublicOnly
        ? $"Key: {_cspp.KeyContainerName} - Public Only"
        : $"Key: {_cspp.KeyContainerName} - Full Key Pair";
}

请注意,您可能希望停止使用Aes,因为这只是一个代码示例(由Microsoft提供),还可以考虑替换使用Rijndael类进行加密。

欧文代码的简化版本:

  • 使用Base64字符串转换(就像Marcin建议的那样)
  • 删除了var关键字
using System.Security.Cryptography;
using System.Text;
namespace Test_RsaKeyEncryption
{
    public class RSA
    {
        private static string _privateKey = null!;
        private static string _publicKey = null!;
        public static void Test_RSA()
        {
            RSACryptoServiceProvider rsa = new();
            _privateKey = rsa.ToXmlString(true);
            _publicKey = rsa.ToXmlString(false);
            Console.WriteLine("Private Key: " + _privateKey + Environment.NewLine);
            Console.WriteLine("Public Key: " + _publicKey + Environment.NewLine);
            string text = "Stringa da Criptare@#[]123123";
            Console.WriteLine("Text to encrypt: " + Environment.NewLine + text + Environment.NewLine);
            string enc = Encrypt(text);
            Console.WriteLine("Encrypted Text: " + Environment.NewLine + enc + Environment.NewLine);
            string dec = Decrypt(enc);
            Console.WriteLine("Decrypted Text: " + Environment.NewLine + dec + Environment.NewLine);
        }
        private static string Encrypt(string data)
        {
            RSACryptoServiceProvider rsa = new();
            rsa.FromXmlString(_publicKey);
            byte[] dataToEncrypt = Encoding.ASCII.GetBytes(data);
            byte[] encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();
            return Convert.ToBase64String(encryptedByteArray);
        }
        private static string Decrypt(string data)
        {
            RSACryptoServiceProvider rsa = new();
            rsa.FromXmlString(_privateKey);
            byte[] dataByte = Convert.FromBase64String(data);
            byte[] decryptedByte = rsa.Decrypt(dataByte, false);
            return Encoding.UTF8.GetString(decryptedByte);
        }
    }
}