C# Language
क्रिप्टोग्राफी (System.Security.Cryptography)
खोज…
एक स्ट्रिंग के सममित प्रामाणिक एन्क्रिप्शन के आधुनिक उदाहरण
क्रिप्टोग्राफी कुछ बहुत कठिन है और विभिन्न उदाहरणों को पढ़ने में बहुत समय बिताने के बाद और यह देखने के लिए कि किसी प्रकार की भेद्यता का परिचय देना कितना आसान है, मुझे मूल रूप से @jbtule द्वारा लिखित एक उत्तर मिला जो मुझे लगता है कि बहुत अच्छा है। पढ़ने का मज़ा लें:
"सममित एन्क्रिप्शन के लिए सामान्य सर्वोत्तम अभ्यास एसोसिएटेड डेटा (AEAD) के साथ प्रमाणित एन्क्रिप्शन का उपयोग करना है, हालांकि यह मानक .net क्रिप्टो लाइब्रेरीज़ का हिस्सा नहीं है। इसलिए पहले उदाहरण में AES256 और उसके बाद HMAC6 का उपयोग करते हैं , फिर एक दो चरण एन्क्रिप्ट करें। मैक , जिसे अधिक ओवरहेड और अधिक कुंजी की आवश्यकता होती है।
दूसरा उदाहरण AES256 के सरल अभ्यास का उपयोग करता है- GCM ओपन सोर्स बाउंसी कैसल (नगेट के माध्यम से) का उपयोग करते हुए।
दोनों उदाहरणों में एक मुख्य कार्य है जो गुप्त संदेश स्ट्रिंग, कुंजी (एस) और एक वैकल्पिक गैर-गुप्त पेलोड और रिटर्न लेता है और गैर-गुप्त डेटा के साथ वैकल्पिक रूप से प्रचलित एन्क्रिप्टेड स्ट्रिंग को प्रमाणित करता है। आदर्श रूप से आप इनका उपयोग 256bit कुंजी (रों) के साथ बेतरतीब ढंग से उत्पन्न NewKey()
देखेंगे।
दोनों उदाहरणों में एक सहायक विधियाँ भी हैं जो कुंजियों को उत्पन्न करने के लिए एक स्ट्रिंग पासवर्ड का उपयोग करती हैं। इन सहायक तरीकों को अन्य उदाहरणों के साथ मेल खाने के लिए एक सुविधा के रूप में प्रदान किया जाता है, हालांकि वे बहुत कम सुरक्षित हैं क्योंकि पासवर्ड की ताकत 256 बिट कुंजी की तुलना में बहुत कमजोर होने वाली है ।
अपडेट: जोड़ा गया byte[]
ओवरलोड होता है, और केवल Gist को स्टैकऑवरफ्लो उत्तर सीमाओं के कारण 4 रिक्त स्थान इंडेंट और एपीआई डॉक्स के साथ पूर्ण स्वरूपण है। "
.NET निर्मित-इनक्रिप्ट (एईएस) -थेन-मैक (HMAC) [Gist]
/*
* This work (Modern Encryption of a String C#, by James Tuley),
* identified by James Tuley, is free of known copyright restrictions.
* https://gist.github.com/4336842
* http://creativecommons.org/publicdomain/mark/1.0/
*/
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Encryption
{
public static class AESThenHMAC
{
private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
//Preconfigured Encryption Parameters
public static readonly int BlockBitSize = 128;
public static readonly int KeyBitSize = 256;
//Preconfigured Password Key Derivation Parameters
public static readonly int SaltBitSize = 64;
public static readonly int Iterations = 10000;
public static readonly int MinPasswordLength = 12;
/// <summary>
/// Helper that generates a random key on each call.
/// </summary>
/// <returns></returns>
public static byte[] NewKey()
{
var key = new byte[KeyBitSize / 8];
Random.GetBytes(key);
return key;
}
/// <summary>
/// Simple Encryption (AES) then Authentication (HMAC) for a UTF8 Message.
/// </summary>
/// <param name="secretMessage">The secret message.</param>
/// <param name="cryptKey">The crypt key.</param>
/// <param name="authKey">The auth key.</param>
/// <param name="nonSecretPayload">(Optional) Non-Secret Payload.</param>
/// <returns>
/// Encrypted Message
/// </returns>
/// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception>
/// <remarks>
/// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize + HMac-Tag(32)) * 1.33 Base64
/// </remarks>
public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey,
byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(secretMessage))
throw new ArgumentException("Secret Message Required!", "secretMessage");
var plainText = Encoding.UTF8.GetBytes(secretMessage);
var cipherText = SimpleEncrypt(plainText, cryptKey, authKey, nonSecretPayload);
return Convert.ToBase64String(cipherText);
}
/// <summary>
/// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message.
/// </summary>
/// <param name="encryptedMessage">The encrypted message.</param>
/// <param name="cryptKey">The crypt key.</param>
/// <param name="authKey">The auth key.</param>
/// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
/// <returns>
/// Decrypted Message
/// </returns>
/// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey,
int nonSecretPayloadLength = 0)
{
if (string.IsNullOrWhiteSpace(encryptedMessage))
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
var cipherText = Convert.FromBase64String(encryptedMessage);
var plainText = SimpleDecrypt(cipherText, cryptKey, authKey, nonSecretPayloadLength);
return plainText == null ? null : Encoding.UTF8.GetString(plainText);
}
/// <summary>
/// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message
/// using Keys derived from a Password (PBKDF2).
/// </summary>
/// <param name="secretMessage">The secret message.</param>
/// <param name="password">The password.</param>
/// <param name="nonSecretPayload">The non secret payload.</param>
/// <returns>
/// Encrypted Message
/// </returns>
/// <exception cref="System.ArgumentException">password</exception>
/// <remarks>
/// Significantly less secure than using random binary keys.
/// Adds additional non secret payload for key generation parameters.
/// </remarks>
public static string SimpleEncryptWithPassword(string secretMessage, string password,
byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(secretMessage))
throw new ArgumentException("Secret Message Required!", "secretMessage");
var plainText = Encoding.UTF8.GetBytes(secretMessage);
var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
return Convert.ToBase64String(cipherText);
}
/// <summary>
/// Simple Authentication (HMAC) and then Descryption (AES) of a UTF8 Message
/// using keys derived from a password (PBKDF2).
/// </summary>
/// <param name="encryptedMessage">The encrypted message.</param>
/// <param name="password">The password.</param>
/// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
/// <returns>
/// Decrypted Message
/// </returns>
/// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
/// <remarks>
/// Significantly less secure than using random binary keys.
/// </remarks>
public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
int nonSecretPayloadLength = 0)
{
if (string.IsNullOrWhiteSpace(encryptedMessage))
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
var cipherText = Convert.FromBase64String(encryptedMessage);
var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
return plainText == null ? null : Encoding.UTF8.GetString(plainText);
}
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
{
//User Error Checks
if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");
if (authKey == null || authKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");
if (secretMessage == null || secretMessage.Length < 1)
throw new ArgumentException("Secret Message Required!", "secretMessage");
//non-secret payload optional
nonSecretPayload = nonSecretPayload ?? new byte[] { };
byte[] cipherText;
byte[] iv;
using (var aes = new AesManaged
{
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
//Use random IV
aes.GenerateIV();
iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Assemble encrypted message and add authentication
using (var hmac = new HMACSHA256(authKey))
using (var encryptedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(encryptedStream))
{
//Prepend non-secret payload if any
binaryWriter.Write(nonSecretPayload);
//Prepend IV
binaryWriter.Write(iv);
//Write Ciphertext
binaryWriter.Write(cipherText);
binaryWriter.Flush();
//Authenticate all data
var tag = hmac.ComputeHash(encryptedStream.ToArray());
//Postpend tag
binaryWriter.Write(tag);
}
return encryptedStream.ToArray();
}
}
public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0)
{
//Basic Usage Error Checks
if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("CryptKey needs to be {0} bit!", KeyBitSize), "cryptKey");
if (authKey == null || authKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("AuthKey needs to be {0} bit!", KeyBitSize), "authKey");
if (encryptedMessage == null || encryptedMessage.Length == 0)
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
using (var hmac = new HMACSHA256(authKey))
{
var sentTag = new byte[hmac.HashSize / 8];
//Calculate Tag
var calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTag.Length);
var ivLength = (BlockBitSize / 8);
//if message length is to small just return null
if (encryptedMessage.Length < sentTag.Length + nonSecretPayloadLength + ivLength)
return null;
//Grab Sent Tag
Array.Copy(encryptedMessage, encryptedMessage.Length - sentTag.Length, sentTag, 0, sentTag.Length);
//Compare Tag with constant time comparison
var compare = 0;
for (var i = 0; i < sentTag.Length; i++)
compare |= sentTag[i] ^ calcTag[i];
//if message doesn't authenticate return null
if (compare != 0)
return null;
using (var aes = new AesManaged
{
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
//Grab IV from message
var iv = new byte[ivLength];
Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, iv.Length);
using (var decrypter = aes.CreateDecryptor(cryptKey, iv))
using (var plainTextStream = new MemoryStream())
{
using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(decrypterStream))
{
//Decrypt Cipher Text from Message
binaryWriter.Write(
encryptedMessage,
nonSecretPayloadLength + iv.Length,
encryptedMessage.Length - nonSecretPayloadLength - iv.Length - sentTag.Length
);
}
//Return Plain Text
return plainTextStream.ToArray();
}
}
}
}
public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
{
nonSecretPayload = nonSecretPayload ?? new byte[] {};
//User Error Checks
if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");
if (secretMessage == null || secretMessage.Length ==0)
throw new ArgumentException("Secret Message Required!", "secretMessage");
var payload = new byte[((SaltBitSize / 8) * 2) + nonSecretPayload.Length];
Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
int payloadIndex = nonSecretPayload.Length;
byte[] cryptKey;
byte[] authKey;
//Use Random Salt to prevent pre-generated weak password attacks.
using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations))
{
var salt = generator.Salt;
//Generate Keys
cryptKey = generator.GetBytes(KeyBitSize / 8);
//Create Non Secret Payload
Array.Copy(salt, 0, payload, payloadIndex, salt.Length);
payloadIndex += salt.Length;
}
//Deriving separate key, might be less efficient than using HKDF,
//but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF.
using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations))
{
var salt = generator.Salt;
//Generate Keys
authKey = generator.GetBytes(KeyBitSize / 8);
//Create Rest of Non Secret Payload
Array.Copy(salt, 0, payload, payloadIndex, salt.Length);
}
return SimpleEncrypt(secretMessage, cryptKey, authKey, payload);
}
public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
{
//User Error Checks
if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");
if (encryptedMessage == null || encryptedMessage.Length == 0)
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
var cryptSalt = new byte[SaltBitSize / 8];
var authSalt = new byte[SaltBitSize / 8];
//Grab Salt from Non-Secret Payload
Array.Copy(encryptedMessage, nonSecretPayloadLength, cryptSalt, 0, cryptSalt.Length);
Array.Copy(encryptedMessage, nonSecretPayloadLength + cryptSalt.Length, authSalt, 0, authSalt.Length);
byte[] cryptKey;
byte[] authKey;
//Generate crypt key
using (var generator = new Rfc2898DeriveBytes(password, cryptSalt, Iterations))
{
cryptKey = generator.GetBytes(KeyBitSize / 8);
}
//Generate auth key
using (var generator = new Rfc2898DeriveBytes(password, authSalt, Iterations))
{
authKey = generator.GetBytes(KeyBitSize / 8);
}
return SimpleDecrypt(encryptedMessage, cryptKey, authKey, cryptSalt.Length + authSalt.Length + nonSecretPayloadLength);
}
}
}
उछालभरी कैसल AES-GCM [Gist]
/*
* This work (Modern Encryption of a String C#, by James Tuley),
* identified by James Tuley, is free of known copyright restrictions.
* https://gist.github.com/4336842
* http://creativecommons.org/publicdomain/mark/1.0/
*/
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Encryption
{
public static class AESGCM
{
private static readonly SecureRandom Random = new SecureRandom();
//Preconfigured Encryption Parameters
public static readonly int NonceBitSize = 128;
public static readonly int MacBitSize = 128;
public static readonly int KeyBitSize = 256;
//Preconfigured Password Key Derivation Parameters
public static readonly int SaltBitSize = 128;
public static readonly int Iterations = 10000;
public static readonly int MinPasswordLength = 12;
/// <summary>
/// Helper that generates a random new key on each call.
/// </summary>
/// <returns></returns>
public static byte[] NewKey()
{
var key = new byte[KeyBitSize / 8];
Random.NextBytes(key);
return key;
}
/// <summary>
/// Simple Encryption And Authentication (AES-GCM) of a UTF8 string.
/// </summary>
/// <param name="secretMessage">The secret message.</param>
/// <param name="key">The key.</param>
/// <param name="nonSecretPayload">Optional non-secret payload.</param>
/// <returns>
/// Encrypted Message
/// </returns>
/// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception>
/// <remarks>
/// Adds overhead of (Optional-Payload + BlockSize(16) + Message + HMac-Tag(16)) * 1.33 Base64
/// </remarks>
public static string SimpleEncrypt(string secretMessage, byte[] key, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(secretMessage))
throw new ArgumentException("Secret Message Required!", "secretMessage");
var plainText = Encoding.UTF8.GetBytes(secretMessage);
var cipherText = SimpleEncrypt(plainText, key, nonSecretPayload);
return Convert.ToBase64String(cipherText);
}
/// <summary>
/// Simple Decryption & Authentication (AES-GCM) of a UTF8 Message
/// </summary>
/// <param name="encryptedMessage">The encrypted message.</param>
/// <param name="key">The key.</param>
/// <param name="nonSecretPayloadLength">Length of the optional non-secret payload.</param>
/// <returns>Decrypted Message</returns>
public static string SimpleDecrypt(string encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
{
if (string.IsNullOrEmpty(encryptedMessage))
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
var cipherText = Convert.FromBase64String(encryptedMessage);
var plainText = SimpleDecrypt(cipherText, key, nonSecretPayloadLength);
return plainText == null ? null : Encoding.UTF8.GetString(plainText);
}
/// <summary>
/// Simple Encryption And Authentication (AES-GCM) of a UTF8 String
/// using key derived from a password (PBKDF2).
/// </summary>
/// <param name="secretMessage">The secret message.</param>
/// <param name="password">The password.</param>
/// <param name="nonSecretPayload">The non secret payload.</param>
/// <returns>
/// Encrypted Message
/// </returns>
/// <remarks>
/// Significantly less secure than using random binary keys.
/// Adds additional non secret payload for key generation parameters.
/// </remarks>
public static string SimpleEncryptWithPassword(string secretMessage, string password,
byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(secretMessage))
throw new ArgumentException("Secret Message Required!", "secretMessage");
var plainText = Encoding.UTF8.GetBytes(secretMessage);
var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
return Convert.ToBase64String(cipherText);
}
/// <summary>
/// Simple Decryption and Authentication (AES-GCM) of a UTF8 message
/// using a key derived from a password (PBKDF2)
/// </summary>
/// <param name="encryptedMessage">The encrypted message.</param>
/// <param name="password">The password.</param>
/// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
/// <returns>
/// Decrypted Message
/// </returns>
/// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
/// <remarks>
/// Significantly less secure than using random binary keys.
/// </remarks>
public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
int nonSecretPayloadLength = 0)
{
if (string.IsNullOrWhiteSpace(encryptedMessage))
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
var cipherText = Convert.FromBase64String(encryptedMessage);
var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
return plainText == null ? null : Encoding.UTF8.GetString(plainText);
}
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key, byte[] nonSecretPayload = null)
{
//User Error Checks
if (key == null || key.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
if (secretMessage == null || secretMessage.Length == 0)
throw new ArgumentException("Secret Message Required!", "secretMessage");
//Non-secret Payload Optional
nonSecretPayload = nonSecretPayload ?? new byte[] { };
//Using random nonce large enough not to repeat
var nonce = new byte[NonceBitSize / 8];
Random.NextBytes(nonce, 0, nonce.Length);
var cipher = new GcmBlockCipher(new AesFastEngine());
var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
cipher.Init(true, parameters);
//Generate Cipher Text With Auth Tag
var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
//Assemble Message
using (var combinedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(combinedStream))
{
//Prepend Authenticated Payload
binaryWriter.Write(nonSecretPayload);
//Prepend Nonce
binaryWriter.Write(nonce);
//Write Cipher Text
binaryWriter.Write(cipherText);
}
return combinedStream.ToArray();
}
}
public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
{
//User Error Checks
if (key == null || key.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
if (encryptedMessage == null || encryptedMessage.Length == 0)
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
using (var cipherStream = new MemoryStream(encryptedMessage))
using (var cipherReader = new BinaryReader(cipherStream))
{
//Grab Payload
var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);
//Grab Nonce
var nonce = cipherReader.ReadBytes(NonceBitSize / 8);
var cipher = new GcmBlockCipher(new AesFastEngine());
var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
cipher.Init(false, parameters);
//Decrypt Cipher Text
var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretPayloadLength - nonce.Length);
var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];
try
{
var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
cipher.DoFinal(plainText, len);
}
catch (InvalidCipherTextException)
{
//Return null if it doesn't authenticate
return null;
}
return plainText;
}
}
public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
{
nonSecretPayload = nonSecretPayload ?? new byte[] {};
//User Error Checks
if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");
if (secretMessage == null || secretMessage.Length == 0)
throw new ArgumentException("Secret Message Required!", "secretMessage");
var generator = new Pkcs5S2ParametersGenerator();
//Use Random Salt to minimize pre-generated weak password attacks.
var salt = new byte[SaltBitSize / 8];
Random.NextBytes(salt);
generator.Init(
PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
salt,
Iterations);
//Generate Key
var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);
//Create Full Non Secret Payload
var payload = new byte[salt.Length + nonSecretPayload.Length];
Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
Array.Copy(salt,0, payload,nonSecretPayload.Length, salt.Length);
return SimpleEncrypt(secretMessage, key.GetKey(), payload);
}
public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
{
//User Error Checks
if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");
if (encryptedMessage == null || encryptedMessage.Length == 0)
throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
var generator = new Pkcs5S2ParametersGenerator();
//Grab Salt from Payload
var salt = new byte[SaltBitSize / 8];
Array.Copy(encryptedMessage, nonSecretPayloadLength, salt, 0, salt.Length);
generator.Init(
PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
salt,
Iterations);
//Generate Key
var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);
return SimpleDecrypt(encryptedMessage, key.GetKey(), salt.Length + nonSecretPayloadLength);
}
}
}
सममित और असममित एन्क्रिप्शन का परिचय
आप एन्क्रिप्टिंग तकनीकों को लागू करके डेटा पारगमन या भंडारण के लिए सुरक्षा में सुधार कर सकते हैं। मूल रूप से System.Security.Cryptography का उपयोग करते समय दो दृष्टिकोण हैं: सममित और असममित।
सममित एन्क्रिप्शन
डेटा परिवर्तन करने के लिए यह विधि एक निजी कुंजी का उपयोग करती है।
पेशेवरों:
- सममित एल्गोरिदम कम संसाधनों का उपभोग करते हैं और असममित लोगों की तुलना में तेज़ होते हैं।
- आपके द्वारा एन्क्रिप्ट किया जा सकने वाला डेटा असीमित है।
विपक्ष:
- एन्क्रिप्शन और डिक्रिप्शन एक ही कुंजी का उपयोग करते हैं। यदि कोई समझौता किया जाता है तो कोई आपके डेटा को डिक्रिप्ट कर सकेगा।
- यदि आप अलग-अलग डेटा के लिए एक अलग गुप्त कुंजी का उपयोग करने का चयन करते हैं, तो आप कई अलग-अलग गुप्त कुंजियों का प्रबंधन कर सकते हैं।
System.Security.Cryptography के तहत आपके पास विभिन्न वर्ग हैं जो सममित एन्क्रिप्शन का प्रदर्शन करते हैं, उन्हें ब्लॉक सिफर के रूप में जाना जाता है :
- AesManaged ( एईएस एल्गोरिथ्म)।
- AesCryptoServiceProvider ( एईएस एल्गोरिथ्म FIPS 140-2 शिकायत )।
- DESCryptoServiceProvider ( डेस एल्गोरिथ्म)।
- RC2CryptoServiceProvider ( रिवरेस्ट सिफर 2 एल्गोरिदम)।
- RijndaelManaged ( एईएस एल्गोरिथ्म)। नोट: RijndaelManaged नहीं है FIPS-197 शिकायत।
- ट्रिपलडीईएस ( ट्रिपलडीईएस एल्गोरिथ्म)।
असममित एन्क्रिप्शन
यह विधि डेटा परिवर्तन करने के लिए सार्वजनिक और निजी कुंजियों के संयोजन का उपयोग करती है।
पेशेवरों:
- यह सममित एल्गोरिदम की तुलना में बड़ी कुंजी का उपयोग करता है, इस प्रकार वे क्रूर बल का उपयोग करके क्रैक होने की कम संभावना है।
- यह गारंटी देना आसान है कि कौन डेटा एन्क्रिप्ट और डिक्रिप्ट करने में सक्षम है क्योंकि यह दो कुंजी (सार्वजनिक और निजी) पर निर्भर है।
विपक्ष:
- डेटा की मात्रा पर एक सीमा है जिसे आप एन्क्रिप्ट कर सकते हैं। प्रत्येक एल्गोरिथ्म के लिए सीमा अलग है और आमतौर पर एल्गोरिथ्म के प्रमुख आकार के साथ आनुपातिक है। उदाहरण के लिए, 1,024 बिट्स की एक प्रमुख लंबाई के साथ एक RSACryptoServiceProvider वस्तु केवल 128 बाइट्स से छोटे संदेश को एन्क्रिप्ट कर सकती है।
- सममित एल्गोरिदम की तुलना में असममित एल्गोरिदम बहुत धीमा है।
System.Security.Cryptography के तहत आपके पास विभिन्न वर्गों तक पहुंच है जो असममित एन्क्रिप्शन का प्रदर्शन करते हैं:
- DSACryptoServiceProvider ( डिजिटल हस्ताक्षर एल्गोरिथ्म एल्गोरिथ्म)
- RSACryptoServiceProvider ( RSA एल्गोरिथ्म एल्गोरिथ्म)
पासवर्ड हैशिंग
पासवर्ड को कभी भी सादे पाठ के रूप में संग्रहीत नहीं किया जाना चाहिए! उन्हें धीमी गति से पासवर्ड हैशिंग एल्गोरिथ्म का उपयोग करके एक बेतरतीब ढंग से उत्पन्न नमक (इंद्रधनुष तालिका हमलों से बचाव के लिए) के साथ हैशेड किया जाना चाहिए। बड़ी संख्या में पुनरावृत्तियों (> 10k) का उपयोग जानवर बल के हमलों को धीमा करने के लिए किया जा सकता है। उपयोगकर्ता को लॉग इन करने में ~ 100ms की देरी स्वीकार्य है, लेकिन लंबे पासवर्ड को तोड़ना मुश्किल बनाता है। कई पुनरावृत्तियों का चयन करते समय आपको अपने आवेदन के लिए अधिकतम सहनीय मूल्य का उपयोग करना चाहिए और इसे बढ़ा देना चाहिए क्योंकि कंप्यूटर का प्रदर्शन बेहतर होता है। आपको बार-बार अनुरोधों को रोकने पर भी विचार करना होगा जो कि DoS के हमले के रूप में इस्तेमाल किया जा सकता है।
जब हैशिंग पहली बार आपके लिए एक नमक उत्पन्न कर सकता है, तो परिणामस्वरूप हैश और नमक को एक फ़ाइल में संग्रहीत किया जा सकता है।
private void firstHash(string userName, string userPassword, int numberOfItterations)
{
Rfc2898DeriveBytes PBKDF2 = new Rfc2898DeriveBytes(userPassword, 8, numberOfItterations); //Hash the password with a 8 byte salt
byte[] hashedPassword = PBKDF2.GetBytes(20); //Returns a 20 byte hash
byte[] salt = PBKDF2.Salt;
writeHashToFile(userName, hashedPassword, salt, numberOfItterations); //Store the hashed password with the salt and number of itterations to check against future password entries
}
एक मौजूदा उपयोगकर्ता पासवर्ड की जाँच, एक फ़ाइल से उनके हैश और नमक पढ़ें और दर्ज पासवर्ड के हैश से तुलना करें
private bool checkPassword(string userName, string userPassword, int numberOfItterations)
{
byte[] usersHash = getUserHashFromFile(userName);
byte[] userSalt = getUserSaltFromFile(userName);
Rfc2898DeriveBytes PBKDF2 = new Rfc2898DeriveBytes(userPassword, userSalt, numberOfItterations); //Hash the password with the users salt
byte[] hashedPassword = PBKDF2.GetBytes(20); //Returns a 20 byte hash
bool passwordsMach = comparePasswords(usersHash, hashedPassword); //Compares byte arrays
return passwordsMach;
}
सरल सममित फ़ाइल एन्क्रिप्शन
निम्न कोड नमूना एईएस सममित एन्क्रिप्शन एल्गोरिथ्म का उपयोग करके फ़ाइलों को एन्क्रिप्ट और डिक्रिप्ट करने का एक त्वरित और आसान साधन प्रदर्शित करता है।
कोड बेतरतीब ढंग से हर बार एक फ़ाइल एन्क्रिप्ट किया गया है, जिसका अर्थ है कि नमक और प्रारंभिक क्षेत्र उत्पन्न करता है, जिसका अर्थ है कि एक ही पासवर्ड के साथ एक ही फ़ाइल को एन्क्रिप्ट करने से हमेशा अलग आउटपुट होगा। नमक और IV आउटपुट फ़ाइल में लिखे जाते हैं ताकि इसे डिक्रिप्ट करने के लिए केवल पासवर्ड की आवश्यकता हो।
public static void ProcessFile(string inputPath, string password, bool encryptMode, string outputPath)
{
using (var cypher = new AesManaged())
using (var fsIn = new FileStream(inputPath, FileMode.Open))
using (var fsOut = new FileStream(outputPath, FileMode.Create))
{
const int saltLength = 256;
var salt = new byte[saltLength];
var iv = new byte[cypher.BlockSize / 8];
if (encryptMode)
{
// Generate random salt and IV, then write them to file
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(salt);
rng.GetBytes(iv);
}
fsOut.Write(salt, 0, salt.Length);
fsOut.Write(iv, 0, iv.Length);
}
else
{
// Read the salt and IV from the file
fsIn.Read(salt, 0, saltLength);
fsIn.Read(iv, 0, iv.Length);
}
// Generate a secure password, based on the password and salt provided
var pdb = new Rfc2898DeriveBytes(password, salt);
var key = pdb.GetBytes(cypher.KeySize / 8);
// Encrypt or decrypt the file
using (var cryptoTransform = encryptMode
? cypher.CreateEncryptor(key, iv)
: cypher.CreateDecryptor(key, iv))
using (var cs = new CryptoStream(fsOut, cryptoTransform, CryptoStreamMode.Write))
{
fsIn.CopyTo(cs);
}
}
}
क्रिप्टोग्राफिक रूप से सुरक्षित रैंडम डेटा
ऐसे समय होते हैं जब फ्रेमवर्क के रैंडम () वर्ग को यादृच्छिक रूप से पर्याप्त नहीं माना जा सकता है, यह देखते हुए कि यह एक पीडो-रैंडम संख्या जनरेटर पर आधारित है। हालांकि, फ्रेमवर्क का क्रिप्टो वर्ग RNGCryptoServiceProvider के रूप में कुछ और मजबूत प्रदान करता है।
निम्नलिखित कोड नमूने प्रदर्शित करते हैं कि क्रिप्टोग्राफिक रूप से सुरक्षित बाइट सरणियों, तारों और संख्याओं को कैसे उत्पन्न किया जाए।
रैंडम बाइट ऐरे
public static byte[] GenerateRandomData(int length)
{
var rnd = new byte[length];
using (var rng = new RNGCryptoServiceProvider())
rng.GetBytes(rnd);
return rnd;
}
रैंडम इंटीजर (वितरण के साथ भी)
public static int GenerateRandomInt(int minVal=0, int maxVal=100)
{
var rnd = new byte[4];
using (var rng = new RNGCryptoServiceProvider())
rng.GetBytes(rnd);
var i = Math.Abs(BitConverter.ToInt32(rnd, 0));
return Convert.ToInt32(i % (maxVal - minVal + 1) + minVal);
}
रैंडम स्ट्रिंग
public static string GenerateRandomString(int length, string allowableChars=null)
{
if (string.IsNullOrEmpty(allowableChars))
allowableChars = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Generate random data
var rnd = new byte[length];
using (var rng = new RNGCryptoServiceProvider())
rng.GetBytes(rnd);
// Generate the output string
var allowable = allowableChars.ToCharArray();
var l = allowable.Length;
var chars = new char[length];
for (var i = 0; i < length; i++)
chars[i] = allowable[rnd[i] % l];
return new string(chars);
}
फास्ट असममित फ़ाइल एन्क्रिप्शन
असममित एन्क्रिप्शन को अक्सर अन्य पार्टियों को संदेश स्थानांतरित करने के लिए सिमेट्रिक एन्क्रिप्शन के लिए बेहतर माना जाता है। यह मुख्य रूप से है क्योंकि यह एक साझा कुंजी के आदान-प्रदान से संबंधित कई जोखिमों को नकारता है और यह सुनिश्चित करता है कि सार्वजनिक कुंजी वाला कोई भी व्यक्ति इच्छित प्राप्तकर्ता के लिए एक संदेश एन्क्रिप्ट कर सकता है, केवल वह प्राप्तकर्ता इसे डिक्रिप्ट कर सकता है। दुर्भाग्य से असममित एन्क्रिप्शन एल्गोरिदम का प्रमुख डाउन साइड यह है कि वे अपने सममित चचेरे भाई की तुलना में काफी धीमी हैं। जैसे कि फाइलों की असममित एन्क्रिप्शन, विशेष रूप से बड़े वाले, अक्सर एक बहुत कम्प्यूटेशनल गहन प्रक्रिया हो सकती है।
सुरक्षा और प्रदर्शन दोनों प्रदान करने के लिए, एक संकर दृष्टिकोण लिया जा सकता है। यह सममित एन्क्रिप्शन के लिए एक कुंजी और आरंभीकरण वेक्टर के क्रिप्टोग्राफिक रूप से यादृच्छिक पीढ़ी को मजबूर करता है। फिर इन मानों एक असममित कलन विधि का उपयोग एन्क्रिप्टेड और आउटपुट फ़ाइल के लिए लिखा, स्रोत डेटा संतुलित रूप एन्क्रिप्ट और उत्पादन के लिए यह जोड़कर करने के लिए इस्तेमाल किया जा रहा से पहले कर रहे हैं।
यह दृष्टिकोण प्रदर्शन और सुरक्षा दोनों का उच्च स्तर प्रदान करता है, जिसमें सममित एल्गोरिथ्म (तेज़) और कुंजी और iv का उपयोग करके डेटा एन्क्रिप्ट किया गया है, दोनों बेतरतीब ढंग से उत्पन्न (सुरक्षित) एक असममित एल्गोरिथ्म (सुरक्षित) द्वारा एन्क्रिप्ट किए गए हैं। इसका अतिरिक्त लाभ यह भी है कि अलग-अलग अवसरों पर एन्क्रिप्ट किए गए एक ही पेलोड में बहुत अलग-अलग साइफ्राटेक्स्ट होंगे, क्योंकि हर बार सममित कुंजी यादृच्छिक रूप से उत्पन्न होती है।
निम्न वर्ग स्ट्रिंग्स और बाइट सरणियों के असममित एन्क्रिप्शन, साथ ही हाइब्रिड फ़ाइल एन्क्रिप्शन को प्रदर्शित करता है।
public static class AsymmetricProvider
{
#region Key Generation
public class KeyPair
{
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
}
public static KeyPair GenerateNewKeyPair(int keySize = 4096)
{
// KeySize is measured in bits. 1024 is the default, 2048 is better, 4096 is more robust but takes a fair bit longer to generate.
using (var rsa = new RSACryptoServiceProvider(keySize))
{
return new KeyPair {PublicKey = rsa.ToXmlString(false), PrivateKey = rsa.ToXmlString(true)};
}
}
#endregion
#region Asymmetric Data Encryption and Decryption
public static byte[] EncryptData(byte[] data, string publicKey)
{
using (var asymmetricProvider = new RSACryptoServiceProvider())
{
asymmetricProvider.FromXmlString(publicKey);
return asymmetricProvider.Encrypt(data, true);
}
}
public static byte[] DecryptData(byte[] data, string publicKey)
{
using (var asymmetricProvider = new RSACryptoServiceProvider())
{
asymmetricProvider.FromXmlString(publicKey);
if (asymmetricProvider.PublicOnly)
throw new Exception("The key provided is a public key and does not contain the private key elements required for decryption");
return asymmetricProvider.Decrypt(data, true);
}
}
public static string EncryptString(string value, string publicKey)
{
return Convert.ToBase64String(EncryptData(Encoding.UTF8.GetBytes(value), publicKey));
}
public static string DecryptString(string value, string privateKey)
{
return Encoding.UTF8.GetString(EncryptData(Convert.FromBase64String(value), privateKey));
}
#endregion
#region Hybrid File Encryption and Decription
public static void EncryptFile(string inputFilePath, string outputFilePath, string publicKey)
{
using (var symmetricCypher = new AesManaged())
{
// Generate random key and IV for symmetric encryption
var key = new byte[symmetricCypher.KeySize / 8];
var iv = new byte[symmetricCypher.BlockSize / 8];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(key);
rng.GetBytes(iv);
}
// Encrypt the symmetric key and IV
var buf = new byte[key.Length + iv.Length];
Array.Copy(key, buf, key.Length);
Array.Copy(iv, 0, buf, key.Length, iv.Length);
buf = EncryptData(buf, publicKey);
var bufLen = BitConverter.GetBytes(buf.Length);
// Symmetrically encrypt the data and write it to the file, along with the encrypted key and iv
using (var cypherKey = symmetricCypher.CreateEncryptor(key, iv))
using (var fsIn = new FileStream(inputFilePath, FileMode.Open))
using (var fsOut = new FileStream(outputFilePath, FileMode.Create))
using (var cs = new CryptoStream(fsOut, cypherKey, CryptoStreamMode.Write))
{
fsOut.Write(bufLen,0, bufLen.Length);
fsOut.Write(buf, 0, buf.Length);
fsIn.CopyTo(cs);
}
}
}
public static void DecryptFile(string inputFilePath, string outputFilePath, string privateKey)
{
using (var symmetricCypher = new AesManaged())
using (var fsIn = new FileStream(inputFilePath, FileMode.Open))
{
// Determine the length of the encrypted key and IV
var buf = new byte[sizeof(int)];
fsIn.Read(buf, 0, buf.Length);
var bufLen = BitConverter.ToInt32(buf, 0);
// Read the encrypted key and IV data from the file and decrypt using the asymmetric algorithm
buf = new byte[bufLen];
fsIn.Read(buf, 0, buf.Length);
buf = DecryptData(buf, privateKey);
var key = new byte[symmetricCypher.KeySize / 8];
var iv = new byte[symmetricCypher.BlockSize / 8];
Array.Copy(buf, key, key.Length);
Array.Copy(buf, key.Length, iv, 0, iv.Length);
// Decript the file data using the symmetric algorithm
using (var cypherKey = symmetricCypher.CreateDecryptor(key, iv))
using (var fsOut = new FileStream(outputFilePath, FileMode.Create))
using (var cs = new CryptoStream(fsOut, cypherKey, CryptoStreamMode.Write))
{
fsIn.CopyTo(cs);
}
}
}
#endregion
#region Key Storage
public static void WritePublicKey(string publicKeyFilePath, string publicKey)
{
File.WriteAllText(publicKeyFilePath, publicKey);
}
public static string ReadPublicKey(string publicKeyFilePath)
{
return File.ReadAllText(publicKeyFilePath);
}
private const string SymmetricSalt = "Stack_Overflow!"; // Change me!
public static string ReadPrivateKey(string privateKeyFilePath, string password)
{
var salt = Encoding.UTF8.GetBytes(SymmetricSalt);
var cypherText = File.ReadAllBytes(privateKeyFilePath);
using (var cypher = new AesManaged())
{
var pdb = new Rfc2898DeriveBytes(password, salt);
var key = pdb.GetBytes(cypher.KeySize / 8);
var iv = pdb.GetBytes(cypher.BlockSize / 8);
using (var decryptor = cypher.CreateDecryptor(key, iv))
using (var msDecrypt = new MemoryStream(cypherText))
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (var srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
public static void WritePrivateKey(string privateKeyFilePath, string privateKey, string password)
{
var salt = Encoding.UTF8.GetBytes(SymmetricSalt);
using (var cypher = new AesManaged())
{
var pdb = new Rfc2898DeriveBytes(password, salt);
var key = pdb.GetBytes(cypher.KeySize / 8);
var iv = pdb.GetBytes(cypher.BlockSize / 8);
using (var encryptor = cypher.CreateEncryptor(key, iv))
using (var fsEncrypt = new FileStream(privateKeyFilePath, FileMode.Create))
using (var csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(privateKey);
}
}
}
#endregion
}
उपयोग का उदाहरण:
private static void HybridCryptoTest(string privateKeyPath, string privateKeyPassword, string inputPath)
{
// Setup the test
var publicKeyPath = Path.ChangeExtension(privateKeyPath, ".public");
var outputPath = Path.Combine(Path.ChangeExtension(inputPath, ".enc"));
var testPath = Path.Combine(Path.ChangeExtension(inputPath, ".test"));
if (!File.Exists(privateKeyPath))
{
var keys = AsymmetricProvider.GenerateNewKeyPair(2048);
AsymmetricProvider.WritePublicKey(publicKeyPath, keys.PublicKey);
AsymmetricProvider.WritePrivateKey(privateKeyPath, keys.PrivateKey, privateKeyPassword);
}
// Encrypt the file
var publicKey = AsymmetricProvider.ReadPublicKey(publicKeyPath);
AsymmetricProvider.EncryptFile(inputPath, outputPath, publicKey);
// Decrypt it again to compare against the source file
var privateKey = AsymmetricProvider.ReadPrivateKey(privateKeyPath, privateKeyPassword);
AsymmetricProvider.DecryptFile(outputPath, testPath, privateKey);
// Check that the two files match
var source = File.ReadAllBytes(inputPath);
var dest = File.ReadAllBytes(testPath);
if (source.Length != dest.Length)
throw new Exception("Length does not match");
if (source.Where((t, i) => t != dest[i]).Any())
throw new Exception("Data mismatch");
}