PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

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