/Utilities/Cryptography/RSA.cs
C# | 169 lines | 67 code | 12 blank | 90 comment | 7 complexity | 96e905fc2873c6bced32f105f6ad6834 MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Security.Cryptography;
- using Delta.Utilities.Xml;
-
-
- namespace Delta.Utilities.Cryptography
- {
-
- #region Summary
- /// <summary>
- /// This class provides very basic RSA functionality that is currently only
- /// used in combination with the AES cryptography class (the reason for this
- /// is mostly speed, encrypting and decrypting with RSA is very slow and
- /// should only be done for a few bytes, not for a long network stream).
- /// The main use of this class is to encrypt the AES seed value from the
- /// client with help of a public RSA key and then decrypt it again on the
- /// server with the private RSA key (which only the server has). From then
- /// on AES is used to speak with the client securely. From time to time the
- /// client can send a new random seed value for AES to keep the stream
- /// protected (e.g. after transmitting more than 10 MB). This technique is
- /// common practice with RSA: http://www.wordiq.com/definition/RSA
- /// <para />
- /// Details on Public-Key encryption can be found here (only supported on
- /// Windows however and very slow, only useful for small data packets, so
- /// not very useful for general networking):
- /// http://msdn.microsoft.com/en-us/library/92f9ye3s.aspx#public_key
- /// <para />
- /// Please note that RSA is not directly supported by .NET and will not
- /// work on any mobile platform or console right away. However, the Delta
- /// Engine provides automatic code conversion and replaces this class with
- /// a managed only version that works on all platforms. However this means
- /// the class should not be touched at all (or you lose multi-platform
- /// functionality).
- /// <para />
- /// If you want to learn more about PGP, RSA, and asymmetric encryption check
- /// out the following links for more information (the SharpPrivacy
- /// project is outdated and the BouncyCastle PGP API is extremely complex
- /// and too much for the Delta Engine as it won't work on all platforms):
- /// http://ledwith.org/2009/08/pgp-decryption-with-c/
- /// http://www.codeproject.com/KB/security/sharpprivacy.aspx?df=100&forumid=15716&exp=0&select=573797
- /// http://sourceforge.net/projects/sharpprivacy/
- /// http://bouncycastle.org/
- /// http://efreedom.com/Question/1-2693769/Need-Example-BouncyCastle-PGP-File-Encryption-CSharp
- /// http://stackoverflow.com/questions/878312/silverlight-encrypting-username-and-password-for-web-service
- /// http://www.dustinhorne.com/post/Asymmetric-Encryption-and-Signing-with-RSA-in-Silverlight.aspx
- /// http://scrypt.codeplex.com/
- /// </summary>
- #endregion
-
- public class RSA
- {
- #region Constants
- /// <summary>
- /// Use the 2048 bit key default value, which is pretty big and secure.
- /// Generating 512 bit or 1024 bit keys is much faster, but those can be
- /// guessed and hacked much quicker (still very hard however).
- /// </summary>
- private const int KeySizeInBits = 2048;
-
- /// <summary>
- /// Data length for packets. Encrypted data always has this length and
- /// you cannot use any input data exceeding this length for encryption.
- /// When decrypting we will get back the original array size however.
- /// </summary>
- public const int DataLength = KeySizeInBits / 8;
- #endregion
-
- #region Private
-
- #region rsaProvider (Private)
- /// <summary>
- /// .NET RSA cryptography provider, which works fine on Windows. We always
- /// use a 2048 bit key and OAEP padding.
- /// </summary>
- private readonly RSACryptoServiceProvider rsaProvider;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create new RSA class instance with the given private or public key.
- /// If privateOrPublicKey is null, both keys will be generated (which can
- /// take a few seconds even on a fast PC).
- /// </summary>
- /// <param name="privateOrPublicKey">
- /// Loaded public or private key xml data. On clients this is usually just
- /// the public key, the server keeps the private key secret. If this is
- /// null both the private and the public key are generated and can be
- /// saved out via Save.
- /// </param>
- public RSA(XmlNode privateOrPublicKey)
- {
- rsaProvider = new RSACryptoServiceProvider(2048);
- if (privateOrPublicKey != null)
- {
- rsaProvider.FromXmlString(privateOrPublicKey.GetWholeDocumentXmlText());
- }
- }
- #endregion
-
- #region Save (Public)
- /// <summary>
- /// Export private key or just the public key to an xml node, which can
- /// be saved locally and used again next program start (much quicker to
- /// load than to generate new keys all the time).
- /// </summary>
- /// <param name="exportPrivateKey">Export the whole private key? Should
- /// only be done on the server, if this is false this method just exports
- /// the public key (to be used by the clients).</param>
- /// <returns>Xml node with exported key data, which can be saved out to
- /// a file like Public.RSA.key or Pivate.RSA.key.</returns>
- public XmlNode Save(bool exportPrivateKey)
- {
- return XmlNode.FromSnippet(rsaProvider.ToXmlString(exportPrivateKey));
- }
- #endregion
-
- #region Encrypt (Public)
- /// <summary>
- /// Encrypt helper method. Please note that you are only allowed to
- /// encrypt a maximum of 256 bytes of data (because of the 2048 bits key).
- /// Anything bigger will result in an exception.
- /// </summary>
- /// <param name="dataToEncrypt">Data we want to encrypt. A good candidate
- /// is an AES key or seed value or a password hash from a user.</param>
- /// <returns>Encrypted data, which is usually a bit longer than the input
- /// data. Only the server with the private key can decrypt this.</returns>
- public byte[] Encrypt(byte[] dataToEncrypt)
- {
- if (dataToEncrypt == null)
- {
- throw new ArgumentNullException("Unable to encrypt, no data is given");
- }
- if (dataToEncrypt.Length >
- KeySizeInBits / 8)
- {
- throw new ArgumentNullException("Unable to encrypt, too much data " +
- "is given. dataToEncrypt.Length is '" +
- dataToEncrypt.Length +
- "', but the maximum amount of data we can encrypt is '" +
- (KeySizeInBits / 8) +
- "' bytes because of the " + KeySizeInBits +
- " bit key!");
- }
- return rsaProvider.Encrypt(dataToEncrypt, true);
- }
- #endregion
-
- #region Decrypt (Public)
- /// <summary>
- /// Decrypt a previously encrypted message. While encryption is possible
- /// with public keys, you will need the private key to decrypt a message
- /// again. This method will fail if only the public key is available
- /// (e.g. on the client). Only call this on the server side.
- /// </summary>
- /// <param name="dataToDecrypt">Data to decrypt</param>
- /// <returns>Decrypted original data the sender gave us</returns>
- public byte[] Decrypt(byte[] dataToDecrypt)
- {
- if (dataToDecrypt == null)
- {
- throw new ArgumentNullException("Unable to decrypt, no data is given");
- }
- return rsaProvider.Decrypt(dataToDecrypt, true);
- }
- #endregion
- }
- }