如何在C#中使用公钥和私钥加密技术
本文关键字:公钥 私钥 加密 技术 | 更新日期: 2023-09-27 18:29:14
我想使用公钥/私钥技术加密数据。我的意思是,用接收方的公钥加密,接收方可以用自己的私钥解密。
我该怎么做?你有什么建议或示例代码吗?
代码示例:
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);
}
}
}