PageRenderTime 183ms CodeModel.GetById 174ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/Utilities/Cryptography/RSA.cs

#
C# | 169 lines | 67 code | 12 blank | 90 comment | 7 complexity | 96e905fc2873c6bced32f105f6ad6834 MD5 | raw file
  1using System;
  2using System.Security.Cryptography;
  3using Delta.Utilities.Xml;
  4
  5
  6namespace Delta.Utilities.Cryptography
  7{
  8
  9	#region Summary
 10	/// <summary>
 11	/// This class provides very basic RSA functionality that is currently only
 12	/// used in combination with the AES cryptography class (the reason for this
 13	/// is mostly speed, encrypting and decrypting with RSA is very slow and
 14	/// should only be done for a few bytes, not for a long network stream).
 15	/// The main use of this class is to encrypt the AES seed value from the
 16	/// client with help of a public RSA key and then decrypt it again on the
 17	/// server with the private RSA key (which only the server has). From then
 18	/// on AES is used to speak with the client securely. From time to time the
 19	/// client can send a new random seed value for AES to keep the stream
 20	/// protected (e.g. after transmitting more than 10 MB). This technique is
 21	/// common practice with RSA: http://www.wordiq.com/definition/RSA
 22	/// <para />
 23	/// Details on Public-Key encryption can be found here (only supported on
 24	/// Windows however and very slow, only useful for small data packets, so
 25	/// not very useful for general networking):
 26	/// http://msdn.microsoft.com/en-us/library/92f9ye3s.aspx#public_key
 27	/// <para />
 28	/// Please note that RSA is not directly supported by .NET and will not
 29	/// work on any mobile platform or console right away. However, the Delta
 30	/// Engine provides automatic code conversion and replaces this class with
 31	/// a managed only version that works on all platforms. However this means
 32	/// the class should not be touched at all (or you lose multi-platform
 33	/// functionality).
 34	/// <para />
 35	/// If you want to learn more about PGP, RSA, and asymmetric encryption check
 36	/// out the following links for more information (the SharpPrivacy
 37	/// project is outdated and the BouncyCastle PGP API is extremely complex
 38	/// and too much for the Delta Engine as it won't work on all platforms):
 39	/// http://ledwith.org/2009/08/pgp-decryption-with-c/
 40	/// http://www.codeproject.com/KB/security/sharpprivacy.aspx?df=100&forumid=15716&exp=0&select=573797
 41	/// http://sourceforge.net/projects/sharpprivacy/
 42	/// http://bouncycastle.org/
 43	/// http://efreedom.com/Question/1-2693769/Need-Example-BouncyCastle-PGP-File-Encryption-CSharp
 44	/// http://stackoverflow.com/questions/878312/silverlight-encrypting-username-and-password-for-web-service
 45	/// http://www.dustinhorne.com/post/Asymmetric-Encryption-and-Signing-with-RSA-in-Silverlight.aspx
 46	/// http://scrypt.codeplex.com/
 47	/// </summary>
 48	#endregion
 49
 50	public class RSA
 51	{
 52		#region Constants
 53		/// <summary>
 54		/// Use the 2048 bit key default value, which is pretty big and secure.
 55		/// Generating 512 bit or 1024 bit keys is much faster, but those can be
 56		/// guessed and hacked much quicker (still very hard however).
 57		/// </summary>
 58		private const int KeySizeInBits = 2048;
 59
 60		/// <summary>
 61		/// Data length for packets. Encrypted data always has this length and
 62		/// you cannot use any input data exceeding this length for encryption.
 63		/// When decrypting we will get back the original array size however.
 64		/// </summary>
 65		public const int DataLength = KeySizeInBits / 8;
 66		#endregion
 67
 68		#region Private
 69
 70		#region rsaProvider (Private)
 71		/// <summary>
 72		/// .NET RSA cryptography provider, which works fine on Windows. We always
 73		/// use a 2048 bit key and OAEP padding.
 74		/// </summary>
 75		private readonly RSACryptoServiceProvider rsaProvider;
 76		#endregion
 77
 78		#endregion
 79
 80		#region Constructors
 81		/// <summary>
 82		/// Create new RSA class instance with the given private or public key.
 83		/// If privateOrPublicKey is null, both keys will be generated (which can
 84		/// take a few seconds even on a fast PC).
 85		/// </summary>
 86		/// <param name="privateOrPublicKey">
 87		/// Loaded public or private key xml data. On clients this is usually just
 88		/// the public key, the server keeps the private key secret. If this is
 89		/// null both the private and the public key are generated and can be
 90		/// saved out via Save.
 91		/// </param>
 92		public RSA(XmlNode privateOrPublicKey)
 93		{
 94			rsaProvider = new RSACryptoServiceProvider(2048);
 95			if (privateOrPublicKey != null)
 96			{
 97				rsaProvider.FromXmlString(privateOrPublicKey.GetWholeDocumentXmlText());
 98			}
 99		}
100		#endregion
101
102		#region Save (Public)
103		/// <summary>
104		/// Export private key or just the public key to an xml node, which can
105		/// be saved locally and used again next program start (much quicker to
106		/// load than to generate new keys all the time).
107		/// </summary>
108		/// <param name="exportPrivateKey">Export the whole private key? Should
109		/// only be done on the server, if this is false this method just exports
110		/// the public key (to be used by the clients).</param>
111		/// <returns>Xml node with exported key data, which can be saved out to
112		/// a file like Public.RSA.key or Pivate.RSA.key.</returns>
113		public XmlNode Save(bool exportPrivateKey)
114		{
115			return XmlNode.FromSnippet(rsaProvider.ToXmlString(exportPrivateKey));
116		}
117		#endregion
118
119		#region Encrypt (Public)
120		/// <summary>
121		/// Encrypt helper method. Please note that you are only allowed to
122		/// encrypt a maximum of 256 bytes of data (because of the 2048 bits key).
123		/// Anything bigger will result in an exception.
124		/// </summary>
125		/// <param name="dataToEncrypt">Data we want to encrypt. A good candidate
126		/// is an AES key or seed value or a password hash from a user.</param>
127		/// <returns>Encrypted data, which is usually a bit longer than the input
128		/// data. Only the server with the private key can decrypt this.</returns>
129		public byte[] Encrypt(byte[] dataToEncrypt)
130		{
131			if (dataToEncrypt == null)
132			{
133				throw new ArgumentNullException("Unable to encrypt, no data is given");
134			}
135			if (dataToEncrypt.Length >
136			    KeySizeInBits / 8)
137			{
138				throw new ArgumentNullException("Unable to encrypt, too much data " +
139				                                "is given. dataToEncrypt.Length is '" +
140				                                dataToEncrypt.Length +
141				                                "', but the maximum amount of data we can encrypt is '" +
142				                                (KeySizeInBits / 8) +
143				                                "' bytes because of the " + KeySizeInBits +
144				                                " bit key!");
145			}
146			return rsaProvider.Encrypt(dataToEncrypt, true);
147		}
148		#endregion
149
150		#region Decrypt (Public)
151		/// <summary>
152		/// Decrypt a previously encrypted message. While encryption is possible
153		/// with public keys, you will need the private key to decrypt a message
154		/// again. This method will fail if only the public key is available
155		/// (e.g. on the client). Only call this on the server side.
156		/// </summary>
157		/// <param name="dataToDecrypt">Data to decrypt</param>
158		/// <returns>Decrypted original data the sender gave us</returns>
159		public byte[] Decrypt(byte[] dataToDecrypt)
160		{
161			if (dataToDecrypt == null)
162			{
163				throw new ArgumentNullException("Unable to decrypt, no data is given");
164			}
165			return rsaProvider.Decrypt(dataToDecrypt, true);
166		}
167		#endregion
168	}
169}