using System; using System.IO; using System.Text; using System.Security.Cryptography; using sscs.common; using sscs.constants; namespace sscs.crypto { public class CASACrypto { private const int SALTSIZE = 64; private const int ITERATION_COUNT = 1000; internal static byte[] Generate16ByteKeyFromString(string sTheString) { byte[] baKey = new byte[16]; //return value try { Rfc2898DeriveBytes pkcs5 = new Rfc2898DeriveBytes(sTheString, SALTSIZE, ITERATION_COUNT); baKey = pkcs5.GetBytes(16); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Key generation failed"); baKey = null; } return baKey; } internal static bool StoreKeySetUsingMasterPasscode(byte[] key, byte[] IV, byte[] baMasterPasscode, string fileName) { bool bRet = false; FileStream fsEncrypt = null; CryptoStream csEncrypt = null; try { //Get an encryptor. RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform encryptor; encryptor = myRijndael.CreateEncryptor(baMasterPasscode, baMasterPasscode); //Encrypt the data to a file fsEncrypt = new FileStream(fileName, FileMode.Create); #if LINUX Mono.Unix.Syscall.chmod(fileName,Mono.Unix.FilePermissions.S_IRUSR | Mono.Unix.FilePermissions.S_IWUSR); #endif SHA256 sha = new SHA256Managed(); byte[] hash = sha.ComputeHash(key); fsEncrypt.Write(hash,0,hash.Length); fsEncrypt.Flush(); csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write); //Write all data to the crypto stream and flush it. csEncrypt.Write(key, 0, key.Length); csEncrypt.FlushFinalBlock(); bRet = true; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Unable to store the generated key"); bRet = false; } if( fsEncrypt != null ) fsEncrypt.Close(); if( csEncrypt != null ) csEncrypt.Close(); return bRet; } internal static byte[] GetKeySetFromFile(byte[] baMasterPasscode, string fileName ) { byte[] baSavedKey = null; FileStream fsDecrypt = null; CryptoStream csDecrypt = null; try { if(!File.Exists(fileName)) { return null; } /* Get a decryptor that uses the same key and IV * as the encryptor. */ RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform decryptor = myRijndael.CreateDecryptor(baMasterPasscode, baMasterPasscode); //Now decrypt fsDecrypt = new FileStream(fileName, FileMode.Open); byte[] storedHash = new byte[32]; fsDecrypt.Read(storedHash,0,storedHash.Length); csDecrypt = new CryptoStream(fsDecrypt, decryptor, CryptoStreamMode.Read); baSavedKey = new byte[32]; //Read the data out of the crypto stream. csDecrypt.Read(baSavedKey, 0, baSavedKey.Length); SHA256 sha = new SHA256Managed(); byte[] newHash = sha.ComputeHash(baSavedKey); for( int i = 0 ; i < 32; i++ ) { if(storedHash[i] != newHash[i]) { CSSSLogger.DbgLog("Hash doesnot match"); fsDecrypt.Close(); csDecrypt.Close(); return null; } } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Unable to get the stored key"); baSavedKey = null; } if ( fsDecrypt != null ) fsDecrypt.Close(); if( csDecrypt != null ) csDecrypt.Close(); return baSavedKey; } internal static void EncryptDataAndWriteToFile(byte[] xmlData, byte[] key, string fileName) { FileStream fsEncrypt = null; CryptoStream csEncrypt = null; try { byte[] IV = new byte[16]; for(int z = 0 ; z < 16; z++ ) IV[z] = key[z]; //Get an encryptor. RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform encryptor = myRijndael.CreateEncryptor(key, IV); //Encrypt the data to a file fsEncrypt = new FileStream(fileName, FileMode.Create); #if LINUX Mono.Unix.Syscall.chmod(fileName,Mono.Unix.FilePermissions.S_IRUSR | Mono.Unix.FilePermissions.S_IWUSR); #endif SHA256 sha = new SHA256Managed(); byte[] hash = sha.ComputeHash(xmlData); fsEncrypt.Write(hash,0,hash.Length); fsEncrypt.Flush(); csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write); //Write all data to the crypto stream and flush it. csEncrypt.Write(xmlData, 0, xmlData.Length); csEncrypt.FlushFinalBlock(); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Encrypting and storing to file failed."); } if( fsEncrypt != null ) fsEncrypt.Close(); if( csEncrypt != null ) csEncrypt.Close(); } internal static byte[] ReadFileAndDecryptData(byte[] key, string fileName) { FileStream fsDecrypt = null; CryptoStream csDecrypt = null; try { byte[] IV = new byte[16]; for(int z = 0 ; z < 16; z++ ) IV[z] = key[z]; //Get a decryptor that uses the same key and IV as the encryptor. RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV); if(!File.Exists(fileName)) { return null; } //Now decrypt fsDecrypt = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] storedHash = new byte[32]; fsDecrypt.Read(storedHash,0,storedHash.Length); csDecrypt = new CryptoStream(fsDecrypt, decryptor, CryptoStreamMode.Read); long fileLen = fsDecrypt.Length - 32; byte[] fromEncrypt = new byte[fileLen]; //Read the data out of the crypto stream. int bytesRead = csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length); byte[] tmpEncrypt = new byte[bytesRead]; for(int i = 0 ; i < bytesRead; i++ ) tmpEncrypt[i] = fromEncrypt[i]; SHA256 sha = new SHA256Managed(); byte[] newHash = sha.ComputeHash(tmpEncrypt); for( int i = 0 ; i < 32; i++ ) { if(storedHash[i] != newHash[i]) { CSSSLogger.DbgLog("Hash doesnot match"); fsDecrypt.Close(); csDecrypt.Close(); return null; } } fsDecrypt.Close(); csDecrypt.Close(); return tmpEncrypt; } catch(Exception e) { Console.WriteLine(e.ToString()); } if( fsDecrypt != null ) { fsDecrypt.Close(); } if( csDecrypt != null ) { csDecrypt.Close(); } return null; } /* The methods EncryptData() and DecryptData() would be * required when we use a database to store secrets. */ /* Encrypts the data with the key and returns the encrypted buffer. */ internal static byte[] EncryptData(byte[] data, byte[] key) { try { byte[] IV = new byte[16]; int i = 0; for(i = 0 ; i < 16; i++ ) IV[i] = key[i]; //Get an encryptor. RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform encryptor = myRijndael.CreateEncryptor(key, IV); MemoryStream ms1 = new MemoryStream(); CryptoStream csEncrypt = new CryptoStream(ms1, encryptor, CryptoStreamMode.Write); //Write all data to the crypto stream and flush it. csEncrypt.Write(data, 0, data.Length); csEncrypt.FlushFinalBlock(); return ms1.ToArray(); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return null; } /* Decrypts the buffer(encrypted) with the key and returns the * decrypted data. */ internal static byte[] DecryptData(byte[] buffer, byte[] key) { try { byte[] IV = new byte[16]; for(int i = 0 ; i < 16; i++ ) IV[i] = key[i]; //Get a decryptor that uses the same key and IV as the encryptor. RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV); MemoryStream ms1 = new MemoryStream(buffer); CryptoStream csDecrypt = new CryptoStream(ms1, decryptor, CryptoStreamMode.Read); byte[] fromEncrypt = new byte[buffer.Length]; //Read the data out of the crypto stream. csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length); return fromEncrypt; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return null; } /* This method checks if we can get the master passcode by * decrypting the passwds file ( where we store all possible * passwds cross-encrypted. * * TBD : As we are storing master passcode and the keys in 2 * different files, we need to take care of cases when 1 of the files * is deleted. */ internal static bool CheckIfMasterPasscodeIsAvailable(string desktopPasswd, string fileName) { return (File.Exists(fileName)); } internal static byte[] GetMasterPasscode(string desktopPasswd, string fileName) { byte[] mp = DecryptMasterPasscodeUsingString(desktopPasswd, fileName); return mp; } /* TBD - There must be a way, where we establish the integrity of * the files where we store the keys and master passcode. * Use a marker ? */ // Used to save the MasterPasscode encrypted with Desktop login, etc internal static void EncryptAndStoreMasterPasscodeUsingString( byte[] baMasterPasscode, string passwd, string fileName) { FileStream fsEncrypt = null; CryptoStream csEncrypt = null; try { if(File.Exists(fileName)) File.Delete(fileName); byte[] baKey = Generate16ByteKeyFromString(passwd); //Get an encryptor. RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform encryptor; encryptor = myRijndael.CreateEncryptor(baKey, baKey); //Encrypt the data to a file fsEncrypt = new FileStream(fileName,FileMode.Create); #if LINUX Mono.Unix.Syscall.chmod(fileName,Mono.Unix.FilePermissions.S_IRUSR | Mono.Unix.FilePermissions.S_IWUSR); #endif csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write); //Write all data to the crypto stream and flush it. csEncrypt.Write(baMasterPasscode, 0, baMasterPasscode.Length); csEncrypt.FlushFinalBlock(); fsEncrypt.Close(); csEncrypt.Close(); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } if( fsEncrypt != null ) { fsEncrypt.Close(); } if( csEncrypt != null ) { csEncrypt.Close(); } } public static byte[] DecryptMasterPasscodeUsingString(string passwd, string fileName) { FileStream fsDecrypt = null; CryptoStream csDecrypt = null; byte[] baSavedMasterPasscode = null; try { byte[] baKey = Generate16ByteKeyFromString(passwd); /* Get a decryptor that uses the same key and * IV as the encryptor. */ RijndaelManaged myRijndael = new RijndaelManaged(); ICryptoTransform decryptor = myRijndael.CreateDecryptor(baKey, baKey); //Now decrypt fsDecrypt = new FileStream(fileName, FileMode.Open); csDecrypt = new CryptoStream(fsDecrypt, decryptor, CryptoStreamMode.Read); baSavedMasterPasscode = new byte[16]; //Read the data out of the crypto stream. csDecrypt.Read(baSavedMasterPasscode, 0, 16); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Unable to decrypt master passode"); baSavedMasterPasscode = null; } if( fsDecrypt != null ) fsDecrypt.Close(); if( csDecrypt != null ) csDecrypt.Close(); return baSavedMasterPasscode; } internal static byte[] GetMasterPasscodeUsingMasterPasswd( string mPasswd, string fileName) { byte[] baMasterPasscode; try { if(File.Exists(fileName)) { /* Decrypt the passcode from the file using master passwd. * and return the decrypted passcode. */ baMasterPasscode = DecryptMasterPasscodeUsingString(mPasswd, fileName); return baMasterPasscode; } else return null; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Failed to get master passcode from master password."); } return null; } internal static byte[] GetMasterPasscodeUsingDesktopPasswd( string desktopPasswd, string fileName) { byte[] passcode; try { if(File.Exists(fileName)) { /* Decrypt the passcode from the file using desktop passwd. * and return the decrypted passcode. */ passcode = DecryptMasterPasscodeUsingString(desktopPasswd, fileName); return passcode; } else return null; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Failed to get master passcode using desktop passwd"); } return null; } //internal static string GenerateMasterPasscodeUsingDesktopPasswd( internal static byte[] GenerateMasterPasscodeUsingString( string desktopPasswd, string fileName, string validationFile, UserIdentifier userId ) { try { byte[] baPasscode; // use AES to generate a random 16 byte key; RijndaelManaged myRijndael = new RijndaelManaged(); myRijndael.KeySize = 128; //Create a new key and initialization vector. myRijndael.GenerateKey(); baPasscode = myRijndael.Key; EncryptAndStoreMasterPasscodeUsingString(baPasscode, desktopPasswd, fileName); EncryptDataAndWriteToFile( Encoding.Default.GetBytes( ConstStrings.MICASA_VALIDATION_STRING), baPasscode, validationFile); return baPasscode; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Generation of master passcode failed."); } return null; } public static bool ValidatePasscode(byte[] baPasscode, string fileName) { /* Here we decrpyt a well known string, throw exception * if not successful * A well-known string is encrpyted by the Passcode and saved */ if ((baPasscode == null) || baPasscode.Length < 1 ) return false; try { byte[] baString = ReadFileAndDecryptData(baPasscode, fileName); string sString = Encoding.Default.GetString(baString); char[] trimChars = {'\0'}; sString = sString.TrimEnd(trimChars); if( ConstStrings.MICASA_VALIDATION_STRING.Equals(sString)) { return true; } else { return false; } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Validation of passcode failed."); } return false; } } }