Chapter150:Cryptography (System.Security.Cryptography)

No Comments

Section150.1:ModernExamplesofSymmetricAuthenticated Encryption of a string

Cryptographyissomethingveryhardandafterspendingalotoftimereadingdifferentexamplesandseeinghow easyitistointroducesomeformofvulnerabilityIfoundanansweroriginallywrittenby@jbtulethatIthinkisvery good. Enjoy reading:

“The general best practice for symmetric encryption is to use Authenticated Encryption with Associated Data (AEAD),howeverthisisn’tapartofthestandard.netcryptolibraries.SothefirstexampleusesAES256andthen HMAC256, a two step Encrypt then MAC, which requires more overhead and more keys.

ThesecondexampleusesthesimplerpracticeofAES256-GCMusingtheopensourceBouncyCastle(vianuget).

Bothexampleshaveamainfunctionthattakessecretmessagestring,key(s)andanoptionalnon-secretpayload and return and authenticated encrypted string optionally prepended with the non-secret data. Ideally you would use these with 256bit key(s) randomly generated see NewKey().

Bothexamplesalsohaveahelpermethodsthatuseastringpasswordtogeneratethekeys.Thesehelpermethods are provided as a convenience to match up with other examples, however they are farlesssecurebecause the strength of the password is going to be far weaker than a 256 bit key.

Update:Addedbyte[]overloads,andonlytheGisthasthefullformattingwith4spacesindentandapidocsdueto StackOverflow answer limits.”

.NETBuilt-inEncrypt(AES)-Then-MAC(HMAC)[Gist]

/*

* Thiswork(ModernEncryptionofaStringC#,byJamesTuley),

* identifiedbyJamesTuley,isfreeofknowncopyrightrestrictions.

* https://gist.github.com/4336842

* http://creativecommons.org/publicdomain/mark/1.0/

*/

usingSystem;

usingSystem.IO;

usingSystem.Security.Cryptography;

usingSystem.Text;

namespaceEncryption

{

publicstaticclassAESThenHMAC

{

privatestaticreadonlyRandomNumberGeneratorRandom=RandomNumberGenerator.Create();

//PreconfiguredEncryptionParameters

publicstaticreadonlyintBlockBitSize=128;

publicstaticreadonlyintKeyBitSize=256;

//PreconfiguredPasswordKeyDerivationParameters

publicstaticreadonlyintSaltBitSize=64;

publicstaticreadonlyintIterations=10000;

publicstaticreadonlyintMinPasswordLength=12;

///<summary>

///Helperthatgeneratesarandomkeyoneachcall.

///</summary>

///<returns></returns>

publicstaticbyte[]NewKey()

{

varkey=newbyte[KeyBitSize/8];

Random.GetBytes(key);

returnkey;

}

///<summary>

///SimpleEncryption(AES)thenAuthentication(HMAC)foraUTF8Message.

///</summary>

///<paramname=”secretMessage”>Thesecretmessage.</param>

///<paramname=”cryptKey”>Thecryptkey.</param>

///<paramname=”authKey”>Theauthkey.</param>

///<paramname=”nonSecretPayload”>(Optional)Non-SecretPayload.</param>

///<returns>

///EncryptedMessage

///</returns>

///<exceptioncref=”System.ArgumentException”>SecretMessageRequired!;secretMessage</exception>

///<remarks>

///Addsoverheadof(Optional-Payload+BlockSize(16)+Message-Padded-To-Blocksize+                                                                                                                                                                                                                        HMac-Tag(32))*1.33Base64

///</remarks>

publicstaticstringSimpleEncrypt(stringsecretMessage,byte[]cryptKey,byte[]authKey, byte[]nonSecretPayload=null)

{

if(string.IsNullOrEmpty(secretMessage))

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”);

varplainText=Encoding.UTF8.GetBytes(secretMessage);

varcipherText=SimpleEncrypt(plainText,cryptKey,authKey,nonSecretPayload); returnConvert.ToBase64String(cipherText);

}

///<summary>

///SimpleAuthentication(HMAC)thenDecryption(AES)forasecretsUTF8Message.

///</summary>

///<paramname=”encryptedMessage”>Theencryptedmessage.</param>

///<paramname=”cryptKey”>Thecryptkey.</param>

///<paramname=”authKey”>Theauthkey.</param>

///<paramname=”nonSecretPayloadLength”>Lengthofthenonsecretpayload.</param>

///<returns>

///DecryptedMessage

///</returns>

///<exceptioncref=”System.ArgumentException”>EncryptedMessage Required!;encryptedMessage</exception>

publicstaticstringSimpleDecrypt(stringencryptedMessage,byte[]cryptKey,byte[]authKey, intnonSecretPayloadLength=0)

{

if(string.IsNullOrWhiteSpace(encryptedMessage))

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

varcipherText=Convert.FromBase64String(encryptedMessage);

varplainText=SimpleDecrypt(cipherText,cryptKey,authKey,nonSecretPayloadLength); returnplainText==null?null:Encoding.UTF8.GetString(plainText);

}

///<summary>

///SimpleEncryption(AES)thenAuthentication(HMAC)ofaUTF8message

///usingKeysderivedfromaPassword(PBKDF2).

///</summary>

///<paramname=”secretMessage”>Thesecretmessage.</param>

///<paramname=”password”>Thepassword.</param>

///<paramname=”nonSecretPayload”>Thenonsecretpayload.</param>

///<returns>

///EncryptedMessage

///</returns>

///<exceptioncref=”System.ArgumentException”>password</exception>

///<remarks>

///Significantlylesssecurethanusingrandombinarykeys.

///Addsadditionalnonsecretpayloadforkeygenerationparameters.

///</remarks>

publicstaticstringSimpleEncryptWithPassword(stringsecretMessage,stringpassword, byte[]nonSecretPayload=null)

{

if(string.IsNullOrEmpty(secretMessage))

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”);

varplainText=Encoding.UTF8.GetBytes(secretMessage);

varcipherText=SimpleEncryptWithPassword(plainText,password,nonSecretPayload); returnConvert.ToBase64String(cipherText);

}

///<summary>

///SimpleAuthentication(HMAC)andthenDescryption(AES)ofaUTF8Message

///usingkeysderivedfromapassword(PBKDF2).

///</summary>

///<paramname=”encryptedMessage”>Theencryptedmessage.</param>

///<paramname=”password”>Thepassword.</param>

///<paramname=”nonSecretPayloadLength”>Lengthofthenonsecretpayload.</param>

///<returns>

///DecryptedMessage

///</returns>

///<exceptioncref=”System.ArgumentException”>EncryptedMessage Required!;encryptedMessage</exception>

///<remarks>

///Significantlylesssecurethanusingrandombinarykeys.

///</remarks>

publicstaticstringSimpleDecryptWithPassword(stringencryptedMessage,stringpassword, intnonSecretPayloadLength=0)

{

if(string.IsNullOrWhiteSpace(encryptedMessage))

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

varcipherText=Convert.FromBase64String(encryptedMessage);

varplainText=SimpleDecryptWithPassword(cipherText,password,nonSecretPayloadLength); returnplainText==null?null:Encoding.UTF8.GetString(plainText);

}

publicstaticbyte[]SimpleEncrypt(byte[]secretMessage,byte[]cryptKey,byte[]authKey, byte[]nonSecretPayload=null)

{

//UserErrorChecks

if(cryptKey==null||cryptKey.Length!=KeyBitSize/8)

thrownewArgumentException(String.Format(“Keyneedstobe{0}bit!”,KeyBitSize), “

cryptKey”);

if(authKey==null||authKey.Length!=KeyBitSize/8)

thrownewArgumentException(String.Format(“Keyneedstobe{0}bit!”,KeyBitSize), “

authKey”);

if(secretMessage==null||secretMessage.Length<1)

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”);

//non-secretpayloadoptional

nonSecretPayload=nonSecretPayload??newbyte[]{};

byte[]cipherText;

byte[]iv;

using(varaes=newAesManaged

{

KeySize=KeyBitSize,

BlockSize=BlockBitSize,Mode=Ci

pherMode.CBC,

Padding=PaddingMode.PKCS7

})

{

//UserandomIV

aes.GenerateIV();

iv=aes.IV;

using(varencrypter=aes.CreateEncryptor(cryptKey,iv))

using(varcipherStream=newMemoryStream())

{

using(varcryptoStream=newCryptoStream(cipherStream,encrypter, CryptoStreamMode.Write))

using(varbinaryWriter=newBinaryWriter(cryptoStream))

{

//EncryptData

binaryWriter.Write(secretMessage);

}

cipherText=cipherStream.ToArray();

}

}

//Assembleencryptedmessageandaddauthentication

using(varhmac=newHMACSHA256(authKey))

using(varencryptedStream=newMemoryStream())

{

using(varbinaryWriter=newBinaryWriter(encryptedStream))

{

//Prependnon-secretpayloadifany

binaryWriter.Write(nonSecretPayload);

//PrependIV

binaryWriter.Write(iv);

//WriteCiphertext

binaryWriter.Write(cipherText);

binaryWriter.Flush();

//Authenticatealldata

vartag=hmac.ComputeHash(encryptedStream.ToArray());

//Postpendtag

binaryWriter.Write(tag);

}

returnencryptedStream.ToArray();

}

}

publicstaticbyte[]SimpleDecrypt(byte[]encryptedMessage,byte[]cryptKey,byte[]authKey, intnonSecretPayloadLength=0)

{

//BasicUsageErrorChecks

if(cryptKey==null||cryptKey.Length!=KeyBitSize/8)

thrownewArgumentException(String.Format(“CryptKeyneedstobe{0}bit!”,KeyBitSize), “cryptKey”);

if(authKey==null||authKey.Length!=KeyBitSize/8)

thrownewArgumentException(String.Format(“AuthKeyneedstobe{0}bit!”,KeyBitSize), “authKey”);

if(encryptedMessage==null||encryptedMessage.Length==0)

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

using(varhmac=newHMACSHA256(authKey))

{

varsentTag=newbyte[hmac.HashSize/8];

//CalculateTag

varcalcTag=hmac.ComputeHash(encryptedMessage,0,encryptedMessage.Length- sentTag.Length);

varivLength=(BlockBitSize/8);

//ifmessagelengthistosmalljustreturnnull

if(encryptedMessage.Length<sentTag.Length+nonSecretPayloadLength+ivLength) return null;

//GrabSentTag

Array.Copy(encryptedMessage,encryptedMessage.Length-sentTag.Length,sentTag,0, sentTag.Length);

//CompareTagwithconstanttimecomparison

varcompare=0;

for(vari=0;i<sentTag.Length;i++)

compare|=sentTag[i]^calcTag[i];

//ifmessagedoesn’tauthenticatereturnnull

if(compare!=0)

returnnull;

using(varaes=newAesManaged

{

KeySize=KeyBitSize,

BlockSize=BlockBitSize,Mode=Ci

pherMode.CBC,

Padding=PaddingMode.PKCS7

})

{

//GrabIVfrommessage

variv=newbyte[ivLength];

Array.Copy(encryptedMessage,nonSecretPayloadLength,iv,0,iv.Length);

using(vardecrypter=aes.CreateDecryptor(cryptKey,iv))

using(varplainTextStream=newMemoryStream())

{

using(vardecrypterStream=newCryptoStream(plainTextStream,decrypter, CryptoStreamMode.Write))

using(varbinaryWriter=newBinaryWriter(decrypterStream))

{

//DecryptCipherTextfromMessage

binaryWriter.Write(encryptedMessage,

nonSecretPayloadLength+iv.Length,

encryptedMessage.Length-nonSecretPayloadLength-iv.Length-sentTag.Length

);

}

//ReturnPlainText

returnplainTextStream.ToArray();

}

}

}

}

publicstaticbyte[]SimpleEncryptWithPassword(byte[]secretMessage,stringpassword,byte[] nonSecretPayload=null)

{

nonSecretPayload=nonSecretPayload??newbyte[]{};

//UserErrorChecks

if(string.IsNullOrWhiteSpace(password)||password.Length<MinPasswordLength) thrownewArgumentException(String.Format(“Musthaveapasswordofatleast{0}

characters!”,MinPasswordLength),”password”);

if(secretMessage==null||secretMessage.Length==0)

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”); varpayload=newbyte[((SaltBitSize/8)*2)+nonSecretPayload.Length];

Array.Copy(nonSecretPayload,payload,nonSecretPayload.Length); intpayloadIndex=nonSecretPayload.Length;

byte[]cryptKey;

byte[]authKey;

//UseRandomSalttopreventpre-generatedweakpasswordattacks.

using(vargenerator=newRfc2898DeriveBytes(password,SaltBitSize/8,Iterations))

{

varsalt=generator.Salt;

//GenerateKeys

cryptKey=generator.GetBytes(KeyBitSize/8);

//CreateNonSecretPayload

Array.Copy(salt,0,payload,payloadIndex,salt.Length);

payloadIndex+=salt.Length;

}

//Derivingseparatekey,mightbelessefficientthanusingHKDF,

//butnowcompatiblewithRNEncryptorwhichhadaverysimilarwireformatandrequiresless codethanHKDF.

using(vargenerator=newRfc2898DeriveBytes(password,SaltBitSize/8,Iterations))

{

varsalt=generator.Salt;

//GenerateKeys

authKey=generator.GetBytes(KeyBitSize/8);

//CreateRestofNonSecretPayload

Array.Copy(salt,0,payload,payloadIndex,salt.Length);

}

returnSimpleEncrypt(secretMessage,cryptKey,authKey,payload);

}

publicstaticbyte[]SimpleDecryptWithPassword(byte[]encryptedMessage,stringpassword,int nonSecretPayloadLength=0)

{

//UserErrorChecks

if(string.IsNullOrWhiteSpace(password)||password.Length<MinPasswordLength) thrownewArgumentException(String.Format(“Musthaveapasswordofatleast{0}

characters!”,MinPasswordLength),”password”);

if(encryptedMessage==null||encryptedMessage.Length==0)

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

varcryptSalt=newbyte[SaltBitSize/8];

varauthSalt=newbyte[SaltBitSize/8];

//GrabSaltfromNon-SecretPayload

Array.Copy(encryptedMessage,nonSecretPayloadLength,cryptSalt,0,cryptSalt.Length); Array.Copy(encryptedMessage,nonSecretPayloadLength+cryptSalt.Length,authSalt,0,

authSalt.Length);

byte[]cryptKey;

byte[]authKey;

//Generatecryptkey

using(vargenerator=newRfc2898DeriveBytes(password,cryptSalt,Iterations))

{

cryptKey=generator.GetBytes(KeyBitSize/8);

}

//Generateauthkey

using(vargenerator=newRfc2898DeriveBytes(password,authSalt,Iterations))

{

authKey=generator.GetBytes(KeyBitSize/8);

}

returnSimpleDecrypt(encryptedMessage,cryptKey,authKey,cryptSalt.Length+authSalt.Length

+nonSecretPayloadLength);

}

}

}

BouncyCastleAES-GCM[Gist]

/*

* Thiswork(ModernEncryptionofaStringC#,byJamesTuley),

* identifiedbyJamesTuley,isfreeofknowncopyrightrestrictions.

* https://gist.github.com/4336842

* http://creativecommons.org/publicdomain/mark/1.0/

*/

usingSystem;

usingSystem.IO;

usingSystem.Text;

usingOrg.BouncyCastle.Crypto;

usingOrg.BouncyCastle.Crypto.Engines;

usingOrg.BouncyCastle.Crypto.Generators;

usingOrg.BouncyCastle.Crypto.Modes;

usingOrg.BouncyCastle.Crypto.Parameters;

usingOrg.BouncyCastle.Security;

namespaceEncryption

{

publicstaticclassAESGCM

{

privatestaticreadonlySecureRandomRandom=newSecureRandom();

//PreconfiguredEncryptionParameters

publicstaticreadonlyintNonceBitSize=128;

publicstaticreadonlyintMacBitSize=128;

publicstaticreadonlyintKeyBitSize=256;

//PreconfiguredPasswordKeyDerivationParameters

publicstaticreadonlyintSaltBitSize=128;

publicstaticreadonlyintIterations=10000;

publicstaticreadonlyintMinPasswordLength=12;

///<summary>

///Helperthatgeneratesarandomnewkeyoneachcall.

///</summary>

///<returns></returns>

publicstaticbyte[]NewKey()

{

varkey=newbyte[KeyBitSize/8];

Random.NextBytes(key);

returnkey;

}

///<summary>

///SimpleEncryptionAndAuthentication(AES-GCM)ofaUTF8string.

///</summary>

///<paramname=”secretMessage”>Thesecretmessage.</param>

///<paramname=”key”>Thekey.</param>

///<paramname=”nonSecretPayload”>Optionalnon-secretpayload.</param>

///<returns>

///EncryptedMessage

///</returns>

///<exceptioncref=”System.ArgumentException”>SecretMessageRequired!;secretMessage</exception>

///<remarks>

///Addsoverheadof(Optional-Payload+BlockSize(16)+Message+                          HMac-Tag(16))*1.33Base64

///</remarks>

publicstaticstringSimpleEncrypt(stringsecretMessage,byte[]key,byte[]nonSecretPayload= null)

{

if(string.IsNullOrEmpty(secretMessage))

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”);

varplainText=Encoding.UTF8.GetBytes(secretMessage);

varcipherText=SimpleEncrypt(plainText,key,nonSecretPayload);

returnConvert.ToBase64String(cipherText);

}

///<summary>

///SimpleDecryption&Authentication(AES-GCM)ofaUTF8Message

///</summary>

///<paramname=”encryptedMessage”>Theencryptedmessage.</param>

///<paramname=”key”>Thekey.</param>

///<paramname=”nonSecretPayloadLength”>Lengthoftheoptionalnon-secretpayload.</param>

///<returns>DecryptedMessage</returns>

publicstaticstringSimpleDecrypt(stringencryptedMessage,byte[]key,intnonSecretPayloadLength=0)

{

if(string.IsNullOrEmpty(encryptedMessage))

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

varcipherText=Convert.FromBase64String(encryptedMessage);

varplainText=SimpleDecrypt(cipherText,key,nonSecretPayloadLength);

returnplainText==null?null:Encoding.UTF8.GetString(plainText);

}

///<summary>

///SimpleEncryptionAndAuthentication(AES-GCM)ofaUTF8String

///usingkeyderivedfromapassword(PBKDF2).

///</summary>

///<paramname=”secretMessage”>Thesecretmessage.</param>

///<paramname=”password”>Thepassword.</param>

///<paramname=”nonSecretPayload”>Thenonsecretpayload.</param>

///<returns>

///EncryptedMessage

///</returns>

///<remarks>

///Significantlylesssecurethanusingrandombinarykeys.

///Addsadditionalnonsecretpayloadforkeygenerationparameters.

///</remarks>

publicstaticstringSimpleEncryptWithPassword(stringsecretMessage,stringpassword, byte[]nonSecretPayload=null)

{

if(string.IsNullOrEmpty(secretMessage))

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”);

varplainText=Encoding.UTF8.GetBytes(secretMessage);

varcipherText=SimpleEncryptWithPassword(plainText,password,nonSecretPayload); returnConvert.ToBase64String(cipherText);

}

///<summary>

///SimpleDecryptionandAuthentication(AES-GCM)ofaUTF8message

///usingakeyderivedfromapassword(PBKDF2)

///</summary>

///<paramname=”encryptedMessage”>Theencryptedmessage.</param>

///<paramname=”password”>Thepassword.</param>

///<paramname=”nonSecretPayloadLength”>Lengthofthenonsecretpayload.</param>

///<returns>

///DecryptedMessage

///</returns>

///<exceptioncref=”System.ArgumentException”>EncryptedMessage Required!;encryptedMessage</exception>

///<remarks>

///Significantlylesssecurethanusingrandombinarykeys.

///</remarks>

publicstaticstringSimpleDecryptWithPassword(stringencryptedMessage,stringpassword, intnonSecretPayloadLength=0)

{

if(string.IsNullOrWhiteSpace(encryptedMessage))

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

varcipherText=Convert.FromBase64String(encryptedMessage);

varplainText=SimpleDecryptWithPassword(cipherText,password,nonSecretPayloadLength); returnplainText==null?null:Encoding.UTF8.GetString(plainText);

}

publicstaticbyte[]SimpleEncrypt(byte[]secretMessage,byte[]key,byte[]nonSecretPayload= null)

{

//UserErrorChecks

if(key==null||key.Length!=KeyBitSize/8)

thrownewArgumentException(String.Format(“Keyneedstobe{0}bit!”,KeyBitSize),”key”);

if(secretMessage==null||secretMessage.Length==0)

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”);

//Non-secretPayloadOptional

nonSecretPayload=nonSecretPayload??newbyte[]{};

//Usingrandomnoncelargeenoughnottorepeat

varnonce=newbyte[NonceBitSize/8];

Random.NextBytes(nonce,0,nonce.Length);

varcipher=newGcmBlockCipher(newAesFastEngine());

varparameters=newAeadParameters(newKeyParameter(key),MacBitSize,nonce, nonSecretPayload);

cipher.Init(true,parameters);

//GenerateCipherTextWithAuthTag

varcipherText=newbyte[cipher.GetOutputSize(secretMessage.Length)];

varlen=cipher.ProcessBytes(secretMessage,0,secretMessage.Length,cipherText,0); cipher.DoFinal(cipherText, len);

//AssembleMessage

using(varcombinedStream=newMemoryStream())

{

using(varbinaryWriter=newBinaryWriter(combinedStream))

{

//PrependAuthenticatedPayload

binaryWriter.Write(nonSecretPayload);

//PrependNonce

binaryWriter.Write(nonce);

//WriteCipherText

binaryWriter.Write(cipherText);

}

returncombinedStream.ToArray();

}

}

publicstaticbyte[]SimpleDecrypt(byte[]encryptedMessage,byte[]key,int nonSecretPayloadLength=0)

{

//UserErrorChecks

if(key==null||key.Length!=KeyBitSize/8)

thrownewArgumentException(String.Format(“Keyneedstobe{0}bit!”,KeyBitSize),”key”);

if(encryptedMessage==null||encryptedMessage.Length==0)

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”);

using(varcipherStream=newMemoryStream(encryptedMessage)) using(varcipherReader=newBinaryReader(cipherStream))

{

//GrabPayload

varnonSecretPayload=cipherReader.ReadBytes(nonSecretPayloadLength);

//GrabNonce

varnonce=cipherReader.ReadBytes(NonceBitSize/8);

varcipher=newGcmBlockCipher(newAesFastEngine());

varparameters=newAeadParameters(newKeyParameter(key),MacBitSize,nonce, nonSecretPayload);

cipher.Init(false,parameters);

//DecryptCipherText

varcipherText=cipherReader.ReadBytes(encryptedMessage.Length-nonSecretPayloadLength- nonce.Length);

varplainText=newbyte[cipher.GetOutputSize(cipherText.Length)];

try

{

varlen=cipher.ProcessBytes(cipherText,0,cipherText.Length,plainText,0); cipher.DoFinal(plainText, len);

}

catch(InvalidCipherTextException)

{

//Returnnullifitdoesn’tauthenticate

returnnull;

}

returnplainText;

}

}

publicstaticbyte[]SimpleEncryptWithPassword(byte[]secretMessage,stringpassword,byte[] nonSecretPayload=null)

{

nonSecretPayload=nonSecretPayload??newbyte[]{};

//UserErrorChecks

if(string.IsNullOrWhiteSpace(password)||password.Length<MinPasswordLength) thrownewArgumentException(String.Format(“Musthaveapasswordofatleast{0}

characters!”,MinPasswordLength),”password”);

if(secretMessage==null||secretMessage.Length==0)

thrownewArgumentException(“SecretMessageRequired!”,”secretMessage”); vargenerator=newPkcs5S2ParametersGenerator();

//UseRandomSalttominimizepre-generatedweakpasswordattacks.

varsalt=newbyte[SaltBitSize/8]; Random.NextBytes(salt);

generator.Init( PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),salt,

Iterations);

//GenerateKey

varkey=(KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);

//CreateFullNonSecretPayload

varpayload=newbyte[salt.Length+nonSecretPayload.Length]; Array.Copy(nonSecretPayload,payload,nonSecretPayload.Length); Array.Copy(salt,0,payload,nonSecretPayload.Length,salt.Length);

returnSimpleEncrypt(secretMessage,key.GetKey(),payload);

}

publicstaticbyte[]SimpleDecryptWithPassword(byte[]encryptedMessage,stringpassword,int nonSecretPayloadLength=0)

{

//UserErrorChecks

if(string.IsNullOrWhiteSpace(password)||password.Length<MinPasswordLength)

thrownewArgumentException(String.Format(“Musthaveapasswordofatleast{0} characters!”,MinPasswordLength),”password”);

if(encryptedMessage==null||encryptedMessage.Length==0)

thrownewArgumentException(“EncryptedMessageRequired!”,”encryptedMessage”); vargenerator=newPkcs5S2ParametersGenerator();

//GrabSaltfromPayload

varsalt=newbyte[SaltBitSize/8];

Array.Copy(encryptedMessage,nonSecretPayloadLength,salt,0,salt.Length);

generator.Init(

PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),salt,

Iterations);

//GenerateKey

varkey=(KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);

returnSimpleDecrypt(encryptedMessage,key.GetKey(),salt.Length+nonSecretPayloadLength);

}

}

}

Section150.2:IntroductiontoSymmetricandAsymmetric Encryption

You can improve the security for data transit or storing by implementing encrypting techniques. Basically there are twoapproacheswhenusingSystem.Security.Cryptography:symmetricandasymmetric.

SymmetricEncryption

Thismethodusesaprivatekeyinordertoperformthedatatransformation. Pros:

Cons:

  • Symmetricalgorithmsconsumelessresourcesandarefasterthanasymmetricones.
  • The amount of data you can encrypt is unlimited.
  • Encryptionanddecryptionusethesamekey.Someonewillbeabletodecryptyourdataifthekeyis compromised.You could end up with many different secret keys to manage if you choose to use a different secret key for
  • different data.

Under System.Security.Cryptography you have different classes that perform symmetric encryption, they are known as block ciphers:

  • AesManaged(AESalgorithm).
  • AesCryptoServiceProvider(AESalgorithm FIPS 140-2 complaint). DESCryptoServiceProvider(DESalgorithm).
  • RC2CryptoServiceProvider(Rivest Cipher 2algorithm).
  • RijndaelManaged(AESalgorithm). Note: RijndaelManaged is notFIPS-197complaint. TripleDES(TripleDESalgorithm).
  • AsymmetricEncryption

Thismethodusesacombinationofpublicandprivatekeysinordertoperformthedatatransformation. Pros:

Cons:

  • Ituseslargerkeysthansymmetricalgorithms,thustheyarelesssusceptibletobeingcrackedbyusingbrute force.
  • Itiseasiertoguaranteewhoisabletoencryptanddecryptthedatabecauseitreliesontwokeys(publicand private).
  • There is a limit on the amount of data that you can encrypt. The limit is different for each algorithm and is typically proportional with the key size of the algorithm. For example, an RSACryptoServiceProvider objectwithakeylengthof1,024bitscanonlyencryptamessagethatissmallerthan128bytes.
  • Asymmetric algorithmsareveryslowincomparison tosymmetricalgorithms.
  • UnderSystem.Security.Cryptographyyouhaveaccesstodifferentclassesthatperformasymmetricencryption: DSACryptoServiceProvider(Digital Signature Algorithmalgorithm)
  • RSACryptoServiceProvider(RSA Algorithmalgorithm)

Section150.3:SimpleSymmetricFileEncryption

ThefollowingcodesampledemonstratesaquickandeasymeansofencryptinganddecryptingfilesusingtheAES symmetric encryption algorithm.

The code randomly generates the Salt and Initialization Vectors each time a file is encrypted, meaning that encrypting the same file with the same password will always lead to different output. The salt and IV are written to the output file so that only the password is required to decrypt it.

publicstaticvoidProcessFile(stringinputPath,stringpassword,boolencryptMode,stringoutputPath)

{

using(varcypher=newAesManaged())

using(varfsIn=newFileStream(inputPath,FileMode.Open)) using(varfsOut=newFileStream(outputPath,FileMode.Create))

{

constintsaltLength=256;

varsalt=newbyte[saltLength];

variv=newbyte[cypher.BlockSize/8];

if(encryptMode)

{

//GeneraterandomsaltandIV,thenwritethemtofile

using(varrng=newRNGCryptoServiceProvider())

{

rng.GetBytes(salt);

rng.GetBytes(iv);

}

fsOut.Write(salt,0,salt.Length);

fsOut.Write(iv,0,iv.Length);

}

else

{

//ReadthesaltandIVfromthefile

fsIn.Read(salt,0,saltLength);

fsIn.Read(iv,0,iv.Length);

}

//Generateasecurepassword,basedonthepasswordandsaltprovided

var pdb = new Rfc2898DeriveBytes(password, salt);

varkey=pdb.GetBytes(cypher.KeySize/8);

//Encryptordecryptthefile

using(varcryptoTransform=encryptMode

?cypher.CreateEncryptor(key,iv)

:cypher.CreateDecryptor(key,iv))

using(varcs=newCryptoStream(fsOut,cryptoTransform,CryptoStreamMode.Write))

{

fsIn.CopyTo(cs);

}

}

}

Section150.4:CryptographicallySecureRandomData

There are times when the framework’s Random() class may not be considered random enough, given that it is based on a psuedo-random number generator. The framework’s Crypto classes do, however, provide something more robust in the form of RNGCryptoServiceProvider.

ThefollowingcodesamplesdemonstratehowtogenerateCryptographicallySecurebytearrays,stringsand numbers.

RandomByteArray

publicstaticbyte[]GenerateRandomData(intlength)

{

varrnd=newbyte[length];

using(varrng=newRNGCryptoServiceProvider()) rng.GetBytes(rnd);

returnrnd;

}

RandomInteger(withevendistribution)

publicstaticintGenerateRandomInt(intminVal=0,intmaxVal=100)

{

varrnd=newbyte[4];

using(varrng=newRNGCryptoServiceProvider()) rng.GetBytes(rnd);

vari=Math.Abs(BitConverter.ToInt32(rnd,0));

returnConvert.ToInt32(i%(maxVal-minVal+1)+minVal);

}

RandomString

publicstaticstringGenerateRandomString(intlength,stringallowableChars=null)

{

if(string.IsNullOrEmpty(allowableChars))

allowableChars=@”ABCDEFGHIJKLMNOPQRSTUVWXYZ”;

//Generaterandomdata

varrnd=newbyte[length];

using(varrng=newRNGCryptoServiceProvider()) rng.GetBytes(rnd);

//Generatetheoutputstring

varallowable=allowableChars.ToCharArray(); var l

=allowable.Length;

var chars = newchar[length];

for(vari =0;i <length;i++)

chars[i]=allowable[rnd[i]%l];

returnnew string(chars);

}

Section150.5:PasswordHashing

Passwords should never be stored as plain text! They should be hashed with a randomly generated salt (to defend againstrainbowtableattacks)usingaslowpasswordhashingalgorithm.Ahighnumberofiterations(>10k)canbe used to slow down brute force attacks. A delay of ~100ms is acceptable to a user logging in, but makes breaking a long password difficult. When choosing a number of iterations you should use the maximum tolerable value foryour application and increase it as computer performance improves. You will also need to consider stopping repeated requests which could be used as a DoS attack.

When hashing for the first time a salt can be generated for you, the resulting hash and salt can then be stored to a file.

privatevoidfirstHash(stringuserName,stringuserPassword,intnumberOfItterations)

{

Rfc2898DeriveBytesPBKDF2=newRfc2898DeriveBytes(userPassword,8,numberOfItterations);

//Hashthepasswordwitha8bytesalt

byte[]hashedPassword=PBKDF2.GetBytes(20);                 //Returnsa20bytehash

byte[]salt=PBKDF2.Salt;

writeHashToFile(userName,hashedPassword,salt,numberOfItterations);//Storethehashed passwordwiththesaltandnumberofitterationstocheckagainstfuturepasswordentries

}

Checking an existing users password, read their hash and salt from a file and compare to the hash of the entered password

privateboolcheckPassword(stringuserName,stringuserPassword,intnumberOfItterations)

{

byte[]usersHash=getUserHashFromFile(userName);

byte[]userSalt=getUserSaltFromFile(userName);

Rfc2898DeriveBytesPBKDF2=newRfc2898DeriveBytes(userPassword,userSalt, numberOfItterations);                                                            //Hashthepasswordwiththeuserssalt

byte[]hashedPassword=PBKDF2.GetBytes(20);                 //Returnsa20bytehash

boolpasswordsMach=comparePasswords(usersHash,hashedPassword);               //Comparesbytearrays

returnpasswordsMach;

}

Section150.6:FastAsymmetricFileEncryption

AsymmetricencryptionisoftenregardedaspreferabletoSymmetricencryptionfortransferringmessagestoother parties.Thisismainlybecauseitnegatesmanyoftherisksrelatedtotheexchangeofasharedkeyandensures thatwhilstanyonewiththepublickeycanencryptamessagefortheintendedrecipient,onlythatrecipientcan decryptit.Unfortunatelythemajordown-sideofasymmetricencryptionalgorithmsisthattheyaresignificantly slowerthantheirsymmetriccousins.Assuchtheasymmetricencryptionoffiles,especiallylargeones,canoftenbe averycomputationallyintensiveprocess.

Inorder toprovide both securityAND performance,a hybrid approachcan betaken. This entailsthe

cryptographicallyrandomgenerationofakeyandinitializationvectorforSymmetricencryption.Thesevaluesare then encrypted using an Asymmetric algorithm and written to the output file, before being used to encrypt the source data Symmetrically and appending it to the output.

This approach provides a high degree of both performance and security, in that the data is encrypted using a symmetric algorithm (fast) and the key and iv, both randomly generated (secure) are encrypted by an asymmetric algorithm (secure). It also has the added advantage that the same payload encrypted on different occasions will have very different cyphertext, because the symmetric keys are randomly generated each time.

The following class demonstrates asymmetric encryption of strings and byte arrays, as well as hybrid file encryption.

publicstaticclassAsymmetricProvider

{

#regionKeyGeneration

publicclassKeyPair

{

publicstringPublicKey{get;set;}

publicstringPrivateKey{get;set;}

}

publicstaticKeyPairGenerateNewKeyPair(intkeySize=4096)

{

//KeySizeismeasuredinbits.1024isthedefault,2048isbetter,4096ismorerobustbut takesafairbitlongertogenerate.

using(varrsa=newRSACryptoServiceProvider(keySize))

{

returnnewKeyPair{PublicKey=rsa.ToXmlString(false),PrivateKey= rsa.ToXmlString(true)};

}

}

#endregion

#regionAsymmetricDataEncryptionandDecryption

publicstaticbyte[]EncryptData(byte[]data,stringpublicKey)

{

using(varasymmetricProvider=newRSACryptoServiceProvider())

{

asymmetricProvider.FromXmlString(publicKey);

returnasymmetricProvider.Encrypt(data,true);

}

}

publicstaticbyte[]DecryptData(byte[]data,stringpublicKey)

{

using(varasymmetricProvider=newRSACryptoServiceProvider())

{

asymmetricProvider.FromXmlString(publicKey);

if(asymmetricProvider.PublicOnly)

thrownewException(“Thekeyprovidedisapublickeyanddoesnotcontainthe privatekeyelementsrequiredfordecryption”);

returnasymmetricProvider.Decrypt(data,true);

}

}

publicstaticstringEncryptString(stringvalue,stringpublicKey)

{

returnConvert.ToBase64String(EncryptData(Encoding.UTF8.GetBytes(value),publicKey));

}

publicstaticstringDecryptString(stringvalue,stringprivateKey)

{

returnEncoding.UTF8.GetString(EncryptData(Convert.FromBase64String(value),privateKey));

}

#endregion

#regionHybridFileEncryptionandDecription

publicstaticvoidEncryptFile(stringinputFilePath,stringoutputFilePath,stringpublicKey)

{

using(varsymmetricCypher=newAesManaged())

{

//GeneraterandomkeyandIVforsymmetricencryption

varkey=newbyte[symmetricCypher.KeySize/8];

variv=newbyte[symmetricCypher.BlockSize/8];

using(varrng=newRNGCryptoServiceProvider())

{

rng.GetBytes(key);

rng.GetBytes(iv);

}

//EncryptthesymmetrickeyandIV

varbuf=newbyte[key.Length+iv.Length];

Array.Copy(key,buf,key.Length);

Array.Copy(iv,0,buf,key.Length,iv.Length);

buf=EncryptData(buf,publicKey);

varbufLen=BitConverter.GetBytes(buf.Length);

andiv

//Symmetricallyencryptthedataandwriteittothefile,alongwiththeencryptedkey

using(varcypherKey=symmetricCypher.CreateEncryptor(key,iv)) using(varfsIn=newFileStream(inputFilePath,FileMode.Open)) using(varfsOut=newFileStream(outputFilePath,FileMode.Create))

using(varcs=newCryptoStream(fsOut,cypherKey,CryptoStreamMode.Write))

{

fsOut.Write(bufLen,0,bufLen.Length);

fsOut.Write(buf,0,buf.Length); fsIn.CopyTo(cs);

}

}

}

publicstaticvoidDecryptFile(stringinputFilePath,stringoutputFilePath,stringprivateKey)

{

using(varsymmetricCypher=newAesManaged())

using(varfsIn=newFileStream(inputFilePath,FileMode.Open))

{

//DeterminethelengthoftheencryptedkeyandIV

varbuf=newbyte[sizeof(int)];

fsIn.Read(buf,0,buf.Length);

varbufLen=BitConverter.ToInt32(buf,0);

//ReadtheencryptedkeyandIVdatafromthefileanddecryptusingtheasymmetric

algorithm

buf=newbyte[bufLen];

fsIn.Read(buf,0,buf.Length);

buf=DecryptData(buf,privateKey);

varkey=newbyte[symmetricCypher.KeySize/8];

variv=newbyte[symmetricCypher.BlockSize/8];

Array.Copy(buf,key,key.Length);Array.Copy(buf,key.

Length, iv,0,iv.Length);

//Decriptthefiledatausingthesymmetricalgorithm

using(varcypherKey=symmetricCypher.CreateDecryptor(key,iv)) using(varfsOut=newFileStream(outputFilePath,FileMode.Create))

using(varcs=newCryptoStream(fsOut,cypherKey,CryptoStreamMode.Write))

{

fsIn.CopyTo(cs);

}

}

}

#endregion

#regionKeyStorage

publicstaticvoidWritePublicKey(stringpublicKeyFilePath,stringpublicKey)

{

File.WriteAllText(publicKeyFilePath,publicKey);

}

publicstaticstringReadPublicKey(stringpublicKeyFilePath)

{

returnFile.ReadAllText(publicKeyFilePath);

}

privateconststringSymmetricSalt=”Stack_Overflow!”;//Changeme!

publicstaticstringReadPrivateKey(stringprivateKeyFilePath,stringpassword)

{

varsalt=Encoding.UTF8.GetBytes(SymmetricSalt);

varcypherText=File.ReadAllBytes(privateKeyFilePath);

using(varcypher=newAesManaged())

{

varpdb=newRfc2898DeriveBytes(password,salt);

varkey=pdb.GetBytes(cypher.KeySize/8);

variv=pdb.GetBytes(cypher.BlockSize/8);

using(vardecryptor=cypher.CreateDecryptor(key,iv)) using(varmsDecrypt=newMemoryStream(cypherText))

using(varcsDecrypt=newCryptoStream(msDecrypt,decryptor,CryptoStreamMode.Read)) using(varsrDecrypt=newStreamReader(csDecrypt))

{

returnsrDecrypt.ReadToEnd();

}

}

}

publicstaticvoidWritePrivateKey(stringprivateKeyFilePath,stringprivateKey,string password)

{

varsalt=Encoding.UTF8.GetBytes(SymmetricSalt);

using(varcypher=newAesManaged())

{

varpdb=newRfc2898DeriveBytes(password,salt);

varkey=pdb.GetBytes(cypher.KeySize/8);

variv=pdb.GetBytes(cypher.BlockSize/8);

using(varencryptor=cypher.CreateEncryptor(key,iv))

using(varfsEncrypt=newFileStream(privateKeyFilePath,FileMode.Create))

using(varcsEncrypt=newCryptoStream(fsEncrypt,encryptor,CryptoStreamMode.Write)) using(varswEncrypt=newStreamWriter(csEncrypt))

{

swEncrypt.Write(privateKey);

}

}

}

#endregion

}

Exampleofuse:

privatestaticvoidHybridCryptoTest(stringprivateKeyPath,stringprivateKeyPassword,string inputPath)

{

//Setupthetest

varpublicKeyPath=Path.ChangeExtension(privateKeyPath,”.public”); varoutputPath=Path.Combine(Path.ChangeExtension(inputPath,”.enc”)); vartestPath=Path.Combine(Path.ChangeExtension(inputPath,”.test”));

if(!File.Exists(privateKeyPath))

{

varkeys=AsymmetricProvider.GenerateNewKeyPair(2048); AsymmetricProvider.WritePublicKey(publicKeyPath,keys.PublicKey); AsymmetricProvider.WritePrivateKey(privateKeyPath,keys.PrivateKey,privateKeyPassword);

}

//Encryptthefile

varpublicKey=AsymmetricProvider.ReadPublicKey(publicKeyPath); AsymmetricProvider.EncryptFile(inputPath,outputPath,publicKey);

//Decryptitagaintocompareagainstthesourcefile

varprivateKey=AsymmetricProvider.ReadPrivateKey(privateKeyPath,privateKeyPassword); AsymmetricProvider.DecryptFile(outputPath,testPath,privateKey);

//Checkthatthetwofilesmatch

varsource=File.ReadAllBytes(inputPath);

vardest=File.ReadAllBytes(testPath);

if(source.Length!=dest.Length)

thrownewException(“Lengthdoesnotmatch”);

if(source.Where((t,i)=>t!=dest[i]).Any())

thrownewException(“Datamismatch”);

}

About us and this blog

We are a digital marketing company with a focus on helping our customers achieve great results across several key areas.

Request a free quote

We offer professional SEO services that help websites increase their organic search score drastically in order to compete for the highest rankings even when it comes to highly competitive keywords.

Subscribe to our newsletter!

More from our blog

See all posts
No Comments

Leave a Comment