Solution Encrypt and decrypt the DataSet using the .NET cryptographic services, and serialize and save the encrypted DataSet to a stream such as a file or network stream.. The sample co
Trang 1[ Team LiB ]
Recipe 5.7 Transmitting a DataSet Securely
Problem
You need to securely send a DataSet over a connection that is not secure
Solution
Encrypt and decrypt the DataSet using the NET cryptographic services, and serialize and save the encrypted DataSet to a stream (such as a file or network stream)
The sample code contains two event handlers:
Encrypt Button.Click
The first Button.Click creates a DataSet and encrypts it using the algorithm
specified by the user and writes the encrypted DataSet to a file
Decrypt Button.Click
The second Button.Click decrypts a file containing a DataSet previously encrypted using an algorithm specified by the user and uses the file to recreate the DataSet previously encrypted
The C# code is shown in Example 5-7
Example 5-7 File: SecureTransmissionForm.cs
// Namespaces, variables, and constants
using System;
using System.Configuration;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Data;
using System.Data.SqlClient;
// Table name constants
private const String ORDERS_TABLE = "Orders";
Trang 2private const String ORDERDETAILS_TABLE = "OrderDetails";
// Relation name constants
private const String ORDERS_ORDERDETAILS_RELATION = "Orders_OrderDetails_Relation";
// Field name constants
private const String ORDERID_FIELD = "OrderID";
private RSACryptoServiceProvider rSAReceiver;
private const int keySize = 128;
// DES key and IV
private Byte[] dESKey = new Byte[]
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
private Byte[] dESIV = new Byte[]
{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18};
// RC2 key and IV
private Byte[] rC2Key = new Byte[]
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
private Byte[] rC2IV = new Byte[]
{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
// Rijndael key and IV
private Byte[] rijndaelKey = new Byte[]
{0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F};
private Byte[] rijndaelIV = new Byte[]
{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F};
// triple DES key and IV
private Byte[] tDESKey = new Byte[]
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
private Byte[] tDESIV = new Byte[]
{0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37};
//
Trang 3[Serializable( )]
internal class EncryptedMessage
{
public byte[] Body; // RC2 encrypted
public byte[] Key; // RSA encrypted RC2 key
public byte[] IV; // RC2 initialization vector
}
private void encryptButton_Click(object sender, System.EventArgs e)
{
DataSet ds = new DataSet( );
SqlDataAdapter da;
// Fill the Order table and add it to the DataSet
da = new SqlDataAdapter("SELECT * FROM Orders",
ConfigurationSettings.AppSettings["Sql_ConnectString"]);
DataTable orderTable = new DataTable(ORDERS_TABLE);
da.FillSchema(orderTable, SchemaType.Source);
da.Fill(orderTable);
ds.Tables.Add(orderTable);
// Fill the OrderDetails table and add it to the DataSet
da = new SqlDataAdapter("SELECT * FROM [Order Details]",
ConfigurationSettings.AppSettings["Sql_ConnectString"]);
DataTable orderDetailTable = new DataTable(ORDERDETAILS_TABLE); da.FillSchema(orderDetailTable, SchemaType.Source);
da.Fill(orderDetailTable);
ds.Tables.Add(orderDetailTable);
// Create a relation between the tables
ds.Relations.Add(ORDERS_ORDERDETAILS_RELATION,
ds.Tables[ORDERS_TABLE].Columns[ORDERID_FIELD],
ds.Tables[ORDERDETAILS_TABLE].Columns[ORDERID_FIELD], true);
// Clear the grid
dataGrid.DataSource = null;
if(rSARadioButton.Checked)
{
Trang 4// Asymmetric algorithm
EncryptedMessage em = new EncryptedMessage( );
// RC2 symmetric algorithm to encode the DataSet
RC2CryptoServiceProvider rC2 = new RC2CryptoServiceProvider( ); rC2.KeySize = keySize;
// Generate RC2 Key and IV
rC2.GenerateKey( );
rC2.GenerateIV( );
// Get the receiver's RSA public key
RSACryptoServiceProvider rSA = new RSACryptoServiceProvider( ); rSA.ImportParameters(rSAReceiver.ExportParameters(false));
try
{
// Encrypt the RC2 key and IV with the receiver's RSA
// public key
em.Key = rSA.Encrypt(rC2.Key, false);
em.IV = rSA.Encrypt(rC2.IV, false);
}
catch(CryptographicException ex)
{
MessageBox.Show(ex.Message, "Securing Transmission",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Cursor.Current = Cursors.WaitCursor;
// Use the CryptoStream to write the encrypted DataSet to the
// MemoryStream
MemoryStream ms = new MemoryStream( );
CryptoStream cs = new CryptoStream(ms, rC2.CreateEncryptor( ), CryptoStreamMode.Write);
ds.WriteXml(cs, XmlWriteMode.WriteSchema);
cs.FlushFinalBlock( );
em.Body = ms.ToArray( );
cs.Close( );
ms.Close( );
// Serialize the encrypted message to a file
Stream s = File.Open(System.IO.Path.GetTempPath( ) +
@"\rsa.dat", FileMode.Create);
Trang 5BinaryFormatter bf = new BinaryFormatter( );
bf.Serialize(s, em);
s.Close( );
Cursor.Current = Cursors.Default;
MessageBox.Show("Encryption complete.",
"Securing Transmission", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
SaveFileDialog sfd;
sfd = new SaveFileDialog( );
sfd.InitialDirectory = System.IO.Path.GetTempPath( ); sfd.Filter = "All files (*.*)|*.*";
sfd.FilterIndex = 0;
if (sfd.ShowDialog( ) == DialogResult.OK)
{
FileStream fsWrite = null;
try
{
fsWrite = new FileStream(sfd.FileName,
FileMode.Create, FileAccess.Write);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,
"Securing Transmission",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
Cursor.Current = Cursors.WaitCursor;
// Symmetric algorithms
byte[] key = null;
byte[] iV = null;
SymmetricAlgorithm sa = null;
if(dESRadioButton.Checked)
Trang 6{
sa = new DESCryptoServiceProvider( );
key = dESKey;
iV = dESIV;
}
else if(rc2RadioButton.Checked)
{
sa = new RC2CryptoServiceProvider( );
sa.KeySize = 128;
key = rC2Key;
iV = rC2IV;
}
else if(rijndaelRadioButton.Checked)
{
sa = new RijndaelManaged( );
key = rijndaelKey;
iV = rijndaelIV;
}
else if(tripleDESRadioButton.Checked)
{
sa = new TripleDESCryptoServiceProvider( ); key = tDESKey;
iV = tDESIV;
}
// Encrypt the DataSet
CryptoStream cs = null;
try
{
cs = new CryptoStream(fsWrite,
sa.CreateEncryptor(key, iV),
CryptoStreamMode.Write);
ds.WriteXml(cs, XmlWriteMode.WriteSchema); cs.Close( );
MessageBox.Show("Encryption complete.",
"Securing Transmission",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
Trang 7MessageBox.Show(ex.Message,
"Securing Transmission",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
finally
{
fsWrite.Close( );
Cursor.Current = Cursors.Default;
}
}
}
}
private void decryptButton_Click(object sender, System.EventArgs e) {
dataGrid.DataSource = null;
DataSet ds = new DataSet( );
if(rSARadioButton.Checked)
{
// Asymmetric algorithm
// Deserialize the encrypted message from a file
Stream s = null;
try
{
s = File.Open(System.IO.Path.GetTempPath( ) +
@"\rsa.dat", FileMode.Open);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Securing Transmission",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
BinaryFormatter bf = new BinaryFormatter( );
EncryptedMessage em = (EncryptedMessage)bf.Deserialize(s);
s.Close( );
// RC2 symmetric algorithm to decode the DataSet
RC2CryptoServiceProvider rC2 = new RC2CryptoServiceProvider( ); rC2.KeySize = keySize;
Trang 8// Decrypt the RC2 key and IV using the receiver's RSA private // key
try
{
rC2.Key = rSAReceiver.Decrypt(em.Key, false);
rC2.IV = rSAReceiver.Decrypt(em.IV, false);
}
catch (CryptographicException ex)
{
MessageBox.Show(ex.Message, "Securing Transmission", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Cursor.Current = Cursors.WaitCursor;
// Put the message body into the MemoryStream
MemoryStream ms = new MemoryStream(em.Body);
// Use the CryptoStream to read the encrypted DataSet from the // MemoryStream
CryptoStream cs = new CryptoStream(ms, rC2.CreateDecryptor( ), CryptoStreamMode.Read);
ds.ReadXml(cs, XmlReadMode.ReadSchema);
cs.Close( );
dataGrid.DataSource = ds.DefaultViewManager;
Cursor.Current = Cursors.Default;
}
else
{
// Symmetric algorithm
OpenFileDialog ofd;
ofd = new OpenFileDialog( );
ofd.InitialDirectory = System.IO.Path.GetTempPath( );
ofd.Filter = "All files (*.*)|*.*";
ofd.FilterIndex = 0;
if (ofd.ShowDialog( ) == DialogResult.OK)
{
FileStream fsRead = null;
try
Trang 9{
fsRead = new FileStream(ofd.FileName,
FileMode.Open, FileAccess.Read);
}
catch(Exception ex)
{
dataGrid.DataSource = null;
MessageBox.Show(ex.Message,
"Securing Transmission",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
Cursor.Current = Cursors.WaitCursor;
SymmetricAlgorithm sa = null;
byte[] key = null;
byte[] iV = null;
if(dESRadioButton.Checked)
{
sa = new DESCryptoServiceProvider( );
key = dESKey;
iV = dESIV;
}
else if(rc2RadioButton.Checked)
{
sa = new RC2CryptoServiceProvider( );
sa.KeySize = 128;
key = rC2Key;
iV = rC2IV;
}
else if(rijndaelRadioButton.Checked)
{
sa = new RijndaelManaged( );
key = rijndaelKey;
iV = rijndaelIV;
}
else if(tripleDESRadioButton.Checked)
{
sa = new TripleDESCryptoServiceProvider( ); key = tDESKey;
iV = tDESIV;
Trang 10}
// Decrypt the stream into the DataSet
CryptoStream cs = null;
try
{
cs = new CryptoStream(fsRead,
sa.CreateDecryptor(key, iV),
CryptoStreamMode.Read);
ds.ReadXml(cs, XmlReadMode.ReadSchema);
cs.Close( );
dataGrid.DataSource = ds.DefaultViewManager;
}
catch(Exception ex)
{
dataGrid.DataSource = null;
MessageBox.Show(ex.Message,
"Securing Transmission",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
finally
{
fsRead.Close( );
Cursor.Current = Cursors.Default;
}
}
}
}
Discussion
Cryptography protects data from being viewed or modified and provides security when transmitting or serializing the data in environments that are otherwise not secure The data can be encrypted, transmitted or serialized in its encrypted state, and later decrypted
If the data is intercepted in its encrypted state, it is much more difficult to access the data because it is necessary to first decrypt it
Encryption algorithms are of two types: symmetric key and asymmetric key A brief overview follows
Symmetric key algorithms use a secret key to both encrypt and decrypt the data Because
Trang 11the same key is used both to encrypt and decrypt the data, it must be kept secret
Symmetric algorithms are also known as secret key algorithms Symmetric key
algorithms are very fast compared to asymmetric algorithms and are therefore suitable for encrypting large amounts of data The NET Framework classes that implement
symmetric key algorithms are:
• DESCryptoServiceProvider
• RC2CryptoServiceProvider
• RijndaelManaged
• TripleDESCryptoServiceProvider
The symmetric key algorithms provided in these classes use an initialization vector (IV)
in addition to the key so that an identical plaintext message produces different ciphertext when using the same key with a different IV Good practice is to use a different IV with each encryption
Asymmetric key algorithms use both a private key that must be kept secret and a public key that can be made available to anyone These key pairs are used both to encrypt data (data encrypted with the public key can only be decrypted with the private key) and sign data (data signed with the private key can only be verified with the public key) The public key is used to encrypt data that is being sent to the owner of the private key while the private key is used to digitally sign data to allow the origin of communication to be verified and to ensure that those communications have not been altered While more secure, asymmetric key algorithms are much slower than symmetric algorithms The .NET Framework classes that implement asymmetric key algorithms are:
• DSACryptoServiceProvider
• RSACryptoServiceProvider
To overcome the performance limitations of asymmetric key algorithms with large
amounts of data and still benefit from the much stronger security they provide, only a symmetric key is encrypted, which is in turn used to encrypt the data Here's how it
works: A public key is obtained from the person to whom the data is being sent A
symmetric key is generated by the sender and subsequently encrypted using the public key received from the recipient The data is then encrypted using the symmetric key; this
is much faster than using the public key The encrypted key and data are then sent to the owner of the public/private key pair The recipient uses the private key to decrypt the symmetric key and can then use the symmetric key to decrypt the data
In the sample code, both the encryption and decryption use the key and IV values defined using variable initializers only as a convenience While the same key and IV values must
be used when encrypting and decrypting data, these values will normally be set according