Mi sono imbattuta sulla crittografia con chiavi asimettriche e dopo essermi informata per giorni e aver letto un po' di documentazione su Msdn, sono riuscita a scrivere questo pezzo di codice che cripta un file usando la chiave pubblica e lo decrypta usando la chiave privata.
(L'esempio è scritto in C# e compilato con il Framework 1.1)
Innanzitutto bisogna ricordarsi di includere la libreria:
using System.Security.Cryptography;
per utilizzare la classe RSACryptoServiceProvider che si occupa di eseguire la crittografia e la decrittografia asimmetrica utilizzando l'implementazione dell'algoritmo RSA fornito dal provider del servizio di crittografia (CSP).
Vediamo come generare le chiavi (chiave pubblica e privata):
private string pubprivkey = Application.StartupPath + @"\pubprivkey.xml";
private string pubkey = Application.StartupPath + @"\pubkey.xml";
private int sizekey = 2048;
public void CreateAndSaveKeys()
{
RSACryptoServiceProvider cryptoProvider = new RSACryptoServiceProvider(sizekey);
SaveKeyInfoAsXml(cryptoProvider, pubprivkey, true); //Salvataggio della chiave privata e pubblica
SaveKeyInfoAsXml(cryptoProvider, pubkey, false); //Salvataggio della chiave pubblica
cryptoProvider.Clear();
}
private void SaveKeyInfoAsXml( AsymmetricAlgorithm cryptoProvider, string fileName, bool includePrivateKey )
{
StreamWriter writer = new StreamWriter(fileName);
writer.Write(cryptoProvider.ToXmlString(includePrivateKey));
writer.Close();
}
Ora vediamo come eseguire la crittografia di un file.
Il metodo che andremo ad usare è "Encrypt" che accetta in ingresso 2 parametri, l'array di byte da crittografare e il parametro OAEP.
La dimensione massima dell'array di byte è di:
(sizekey/ 8) - 11 se il parametro OAEP è false
(sizekey / 8) - 41 se il parametro OAEP è true
quindi per crittografare il contenuto di un file, dovremmo leggere il nostro file e caricarlo in un'array di byte e suddividerlo in sottoarray da passare al metodo Encrypt per crittografare i nostri dati.
Ovviamente per crittografare il nostro file ci sarà bisogno della chiave pubblica.
private void Encrypt( string inputfile, string outputfile )
{
RSACryptoServiceProvider cryptoProvider = new RSACryptoServiceProvider();
cryptoProvider.FromXmlString(GetKeyInfoAsXml(pubkey));
byte[] data = LeggiDati(inputfile);
try
{
EncryptString(cryptoProvider, data, outputfile);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.ToString());
}
cryptoProvider.Clear();
}
private string GetKeyInfoAsXml( string fileName )
{
StreamReader reader = new StreamReader(fileName);
string keyInfo = reader.ReadToEnd();
reader.Close();
return(keyInfo);
}
private byte[] LeggiDati( string fileName )
{
FileInfo fileInfo = new FileInfo(fileName);
BinaryReader reader = new BinaryReader(File.OpenRead(fileName));
byte[] data = new byte[fileInfo.Length];
reader.Read(data, 0, data.Length);
reader.Close();
return(data);
}
public void EncryptString(RSACryptoServiceProvider cryptoProvider, byte[] bytes, string filename )
{
int maxLength = (sizekey/8)-11;
int dataLength = bytes.Length;
int iterations = dataLength / maxLength;
byte [] totalbytes = new byte[0]; //ARRAY CONTENENTE LA CRITTOGRAFIA TOTALE DEL FILE
for( int i = 0; i <= iterations; i++ )
{
byte[] tempBytes = new byte[
( dataLength - maxLength * i > maxLength ) ? maxLength :
dataLength - maxLength * i ];
Buffer.BlockCopy( bytes, maxLength * i, tempBytes, 0,
tempBytes.Length );
byte[] encryptedBytes = cryptoProvider.Encrypt( tempBytes, false ); //applico la crittografia RSA al mio sottoarray
//UNISCO IL SOTTOARRAY CRIPTATO AL RESTO
byte [] tempbyte = new byte[totalbytes.Length + encryptedBytes.Length];
int indice = 0;
for (int j=0; j< totalbytes.Length; j++)
{
tempbyte[indice] = totalbytes[j];
indice++;
}
for (int j=0; j<encryptedBytes.Length; j++)
{
tempbyte[indice] = encryptedBytes[j];
indice++;
}
totalbytes = tempbyte;
}
ScriviDatiB64(filename, totalbytes); //SCRITTURA DEL FILE CRITTOGRAFATO
}
private void ScriviDatiB64( string fileName, byte[] data )
{
ASCIIEncoding encoding = new ASCIIEncoding();
long arrayLength = (long) ((4.0d/3.0d) * data.Length);
if (arrayLength % 4 != 0)
arrayLength += 4 - arrayLength % 4;
char [] base64 = new char[arrayLength];
System.Convert.ToBase64CharArray(data, 0, data.Length, base64, 0);
BinaryWriter writer;
FileStream fs;
if (File.Exists(fileName))
fs= new FileStream(fileName, FileMode.Append, FileAccess.Write);
else
fs= new FileStream(fileName, FileMode.CreateNew, FileAccess.Write);
writer = new BinaryWriter(fs);
writer.Write(base64);
writer.Close();
}
Come si può vedere il file crittografato viene scritto in base64.
A questo punto abbiamo il nostro file salvato e crittografato, ora dobbiamo verificarlo ed estrarre l'originale.
Il metodo che andremo ad usare è "Decrypt" che accetta in ingresso 2 parametri, l'array di byte da crittografare e il parametro OAEP.
La dimensione massima dell'array di byte è di:
(sizekey/ 8) se il parametro OAEP è false
(sizekey / 8) se il parametro OAEP è true
quindi, come per la crittografia, dovremmo leggere il nostro
file e caricarlo in un'array di byte e suddividerlo in sottoarray da
passare al metodo Decrypt per decrittografare i nostri dati.
In questo caso è necessaria la chiave privata che andremmo a leggere dal relativo file.
private void VerifyFile( string outputfile, string inputfile)
{
try
{
RSACryptoServiceProvider cryptoProvider = new RSACryptoServiceProvider();
cryptoProvider.FromXmlString(GetKeyInfoAsXml(pubprivkey));
byte[] dati = LeggiDatiB64(inputfile);
DecryptString(cryptoProvider, dati, outputfile);
cryptoProvider.Clear();
}
catch (System.Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private byte[] LeggiDatiB64( string fileName )
{
FileInfo fileInfo = new FileInfo(fileName);
BinaryReader reader = new BinaryReader(File.OpenRead(fileName));
char[] data = new char[fileInfo.Length];
reader.Read(data, 0, data.Length);
reader.Close();
ASCIIEncoding encoding = new ASCIIEncoding();
byte [] dataout = System.Convert.FromBase64CharArray(data, 0, data.Length);
return(dataout);
}
public void DecryptString(RSACryptoServiceProvider cryptoProvider, byte [] bytes, string filename)
{
int maxLength = (sizekey/8);
int dataLength = bytes.Length;
int iterations = dataLength / maxLength;
byte [] totalbytes = new byte[0];
for( int i = 0; i < iterations; i++ )
{
byte[] tempBytes = new byte[
( dataLength - maxLength * i > maxLength ) ? maxLength :
dataLength - maxLength * i ];
Buffer.BlockCopy( bytes, maxLength * i, tempBytes, 0,
tempBytes.Length );
byte[] encryptedBytes = cryptoProvider.Decrypt( tempBytes, false );
byte [] tempbyte = new byte[totalbytes.Length + encryptedBytes.Length];
int indice = 0;
for (int j=0; j< totalbytes.Length; j++)
{
tempbyte[indice] = totalbytes[j];
indice++;
}
for (int j=0; j<encryptedBytes.Length; j++)
{
tempbyte[indice] = encryptedBytes[j];
indice++;
}
totalbytes = tempbyte;
}
ScriviDati(filename, totalbytes);
}
private void ScriviDati( string fileName, byte[] data )
{
BinaryWriter writer;
FileStream fs;
if (File.Exists(fileName))
fs= new FileStream(fileName, FileMode.Append, FileAccess.Write);
else
fs= new FileStream(fileName, FileMode.CreateNew, FileAccess.Write);
writer = new BinaryWriter(fs);
writer.Write(data);
writer.Close();
}
Vi allego anche la Classe (togliete l'estensione .doc al file allegato per avere la classe).
Spero vi sia di aiuto!
Ciao a tutti.
Un ringraziamento a Marcello Cantelmo per l'aiuto durante lo sviluppo