2016-01-06 14:47:38 +00:00
using System ;
using System.IO ;
using System.Security.Cryptography ;
using System.Text ;
2018-11-30 11:03:21 +00:00
using System.Windows.Forms ;
2018-12-03 08:12:19 +00:00
using System.Collections.Generic ;
2016-01-06 14:47:38 +00:00
2016-01-09 20:46:25 +00:00
namespace Crypto_Notepad
2016-01-06 14:47:38 +00:00
{
2018-11-22 06:19:39 +00:00
/// <summary>
/// This class extracts information needed for decryption from a .cnp file
/// </summary>
class AESMetadata
{
/// <summary>
/// Offset to actual AES data; size of metadata
/// </summary>
2018-12-03 08:12:19 +00:00
public int OffsetToData { get ; private set ; }
public byte [ ] InitialVector { get ; private set ; }
public byte [ ] Salt { get ; private set ; }
2018-11-22 06:19:39 +00:00
public AESMetadata ( )
{
2018-12-03 08:12:19 +00:00
this . InitialVector = new byte [ 16 ] ;
this . Salt = null ;
2018-11-22 06:19:39 +00:00
}
public void DeleteMetadataFromBuffer ( ref byte [ ] rawData )
{
2018-12-03 08:12:19 +00:00
byte [ ] buffer = new byte [ rawData . Length - this . OffsetToData ] ;
System . Buffer . BlockCopy ( rawData , this . OffsetToData , buffer , 0 , rawData . Length - this . OffsetToData ) ;
2018-11-22 06:19:39 +00:00
rawData = buffer ;
}
2018-12-03 08:12:19 +00:00
private bool ReadData ( byte [ ] rawData , int offset , ref byte [ ] dataOut )
2018-11-22 06:19:39 +00:00
{
2018-12-03 08:12:19 +00:00
// Buffer to store bytes
List < byte > buffer = new List < byte > ( ) ;
const byte nullTerminator = 0 ;
bool foundData = false ;
// Push data to buffer
for ( int i = offset ; i < rawData . Length ; i + + ) {
if ( rawData [ i ] = = nullTerminator ) { foundData = true ; break ; }
else { buffer . Add ( rawData [ i ] ) ; }
2018-11-22 06:19:39 +00:00
}
2018-11-30 11:03:21 +00:00
2018-12-03 08:12:19 +00:00
if ( foundData = = true )
2018-11-22 06:19:39 +00:00
{
2018-12-03 08:12:19 +00:00
dataOut = buffer . ToArray ( ) ;
return true ;
2018-11-22 06:19:39 +00:00
}
2018-12-03 08:12:19 +00:00
return false ;
}
public bool GetMetadata ( byte [ ] rawData )
{
int offset = 0 ;
byte [ ] buffer = null ;
if ( ! this . ReadData ( rawData , 0 , ref buffer ) ) { return false ; }
this . InitialVector = buffer ;
offset + = buffer . Length + 1 ;
if ( ! this . ReadData ( rawData , offset , ref buffer ) ) { return false ; }
this . Salt = buffer ;
offset + = buffer . Length + 1 ;
2018-11-22 06:19:39 +00:00
2018-12-03 08:12:19 +00:00
this . OffsetToData = offset ;
2018-11-30 11:03:21 +00:00
return true ;
2018-11-22 06:19:39 +00:00
}
}
2016-01-06 14:47:38 +00:00
class AES
{
2018-11-29 23:21:45 +00:00
public static string Encrypt ( string plainText , string password ,
2018-11-30 11:03:21 +00:00
string salt = null , string hashAlgorithm = "SHA1" ,
2018-11-22 06:19:39 +00:00
int passwordIterations = 2 , int keySize = 256 )
2016-01-06 14:47:38 +00:00
{
if ( string . IsNullOrEmpty ( plainText ) )
return "" ;
2018-11-30 11:03:21 +00:00
byte [ ] plainTextBytes ;
byte [ ] saltValueBytes ;
// In case user wants a random salt or salt is null/empty for some other reason
if ( string . IsNullOrEmpty ( salt ) )
{
saltValueBytes = new byte [ 64 ] ; // Nice and long
RandomNumberGenerator rng = RandomNumberGenerator . Create ( ) ;
rng . GetNonZeroBytes ( saltValueBytes ) ;
2018-12-01 03:03:03 +00:00
rng . Dispose ( ) ;
2018-11-30 11:03:21 +00:00
}
else
{
saltValueBytes = Encoding . ASCII . GetBytes ( salt ) ;
}
plainTextBytes = Encoding . UTF8 . GetBytes ( plainText ) ;
2016-01-06 14:47:38 +00:00
PasswordDeriveBytes derivedPassword = new PasswordDeriveBytes
( password , saltValueBytes , hashAlgorithm , passwordIterations ) ;
2018-11-30 11:03:21 +00:00
// Null password; adds *some* memory dump protection
password = null ;
2016-01-06 14:47:38 +00:00
byte [ ] keyBytes = derivedPassword . GetBytes ( keySize / 8 ) ;
RijndaelManaged symmetricKey = new RijndaelManaged ( ) ;
symmetricKey . Mode = CipherMode . CBC ;
2018-11-29 23:21:45 +00:00
symmetricKey . GenerateIV ( ) ;
2016-01-06 14:47:38 +00:00
byte [ ] cipherTextBytes = null ;
2018-11-22 06:19:39 +00:00
using ( MemoryStream memStream = new MemoryStream ( ) )
2016-01-06 14:47:38 +00:00
{
2018-11-22 06:19:39 +00:00
byte [ ] nullByte = { 0 } ;
2018-11-29 23:21:45 +00:00
memStream . Write ( symmetricKey . IV , 0 , symmetricKey . IV . Length ) ;
2018-11-22 06:19:39 +00:00
memStream . Write ( nullByte , 0 , 1 ) ;
memStream . Write ( saltValueBytes , 0 , saltValueBytes . Length ) ;
memStream . Write ( nullByte , 0 , 1 ) ;
using ( ICryptoTransform encryptor = symmetricKey . CreateEncryptor
2018-11-29 23:21:45 +00:00
( keyBytes , symmetricKey . IV ) )
2016-01-06 14:47:38 +00:00
{
using ( CryptoStream cryptoStream = new CryptoStream
( memStream , encryptor , CryptoStreamMode . Write ) )
{
cryptoStream . Write ( plainTextBytes , 0 , plainTextBytes . Length ) ;
cryptoStream . FlushFinalBlock ( ) ;
cipherTextBytes = memStream . ToArray ( ) ;
memStream . Close ( ) ;
cryptoStream . Close ( ) ;
}
}
}
2018-11-22 06:19:39 +00:00
symmetricKey . Dispose ( ) ;
2016-01-06 14:47:38 +00:00
return Convert . ToBase64String ( cipherTextBytes ) ;
}
2018-11-30 11:03:21 +00:00
public static string Decrypt ( string cipherText , string password , string salt = "Kosher" ,
2018-11-22 06:19:39 +00:00
string hashAlgorithm = "SHA1" ,
int passwordIterations = 2 ,
2016-01-06 14:47:38 +00:00
int keySize = 256 )
{
if ( string . IsNullOrEmpty ( cipherText ) )
2018-11-30 11:03:21 +00:00
return null ;
2016-01-06 14:47:38 +00:00
2018-11-22 06:19:39 +00:00
byte [ ] initialVectorBytes ;
byte [ ] saltValueBytes ;
2016-01-06 14:47:38 +00:00
byte [ ] cipherTextBytes = Convert . FromBase64String ( cipherText ) ;
2018-11-22 06:19:39 +00:00
// Extract metadata from file
AESMetadata metadata = new AESMetadata ( ) ;
2018-11-30 11:03:21 +00:00
if ( ! metadata . GetMetadata ( cipherTextBytes ) )
{
// Metadata parsing error
DialogResult result = MessageBox . Show ( "Unable to parse file metadata.\nAttempt to open anyway?\n(May result in a \'Incorrect Key\' error if the salt is wrong.)" ,
"Missing or Corrupted Metadata" , MessageBoxButtons . YesNoCancel , MessageBoxIcon . Asterisk ) ;
if ( result = = DialogResult . Yes )
{
// Default initialization vector from builds v1.1.2 and older
const string default_IV = "16CHARSLONG12345" ;
initialVectorBytes = Encoding . ASCII . GetBytes ( default_IV ) ;
saltValueBytes = Encoding . ASCII . GetBytes ( salt ) ;
}
else { return null ; }
}
else
{
2018-12-03 08:12:19 +00:00
saltValueBytes = metadata . Salt ;
initialVectorBytes = metadata . InitialVector ;
2018-11-30 11:03:21 +00:00
metadata . DeleteMetadataFromBuffer ( ref cipherTextBytes ) ;
}
2018-11-22 06:19:39 +00:00
2016-01-06 14:47:38 +00:00
PasswordDeriveBytes derivedPassword = new PasswordDeriveBytes
2018-11-22 06:19:39 +00:00
( password , saltValueBytes , hashAlgorithm , passwordIterations ) ;
2016-01-06 14:47:38 +00:00
byte [ ] keyBytes = derivedPassword . GetBytes ( keySize / 8 ) ;
RijndaelManaged symmetricKey = new RijndaelManaged ( ) ;
symmetricKey . Mode = CipherMode . CBC ;
byte [ ] plainTextBytes = new byte [ cipherTextBytes . Length ] ;
int byteCount = 0 ;
2018-11-22 06:19:39 +00:00
using ( MemoryStream memStream = new MemoryStream ( cipherTextBytes ) )
2016-01-06 14:47:38 +00:00
{
2018-11-22 06:19:39 +00:00
using ( ICryptoTransform decryptor = symmetricKey . CreateDecryptor
( keyBytes , initialVectorBytes ) )
2016-01-06 14:47:38 +00:00
{
using ( CryptoStream cryptoStream
= new CryptoStream ( memStream , decryptor , CryptoStreamMode . Read ) )
{
byteCount = cryptoStream . Read ( plainTextBytes , 0 , plainTextBytes . Length ) ;
memStream . Close ( ) ;
cryptoStream . Close ( ) ;
}
}
2018-11-22 06:19:39 +00:00
symmetricKey . Dispose ( ) ;
}
2016-01-06 14:47:38 +00:00
return Encoding . UTF8 . GetString ( plainTextBytes , 0 , byteCount ) ;
}
}
}