public void SecureFile_Stream_NoContent() { string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedMemoryStream original = new EnhancedMemoryStream(); EnhancedMemoryStream encrypted = new EnhancedMemoryStream(); EnhancedMemoryStream decrypted = new EnhancedMemoryStream(); SecureFile secure = null; secure = new SecureFile(original, SecureFileMode.Encrypt, publicKey); original.Position = 0; secure.EncryptTo(encrypted, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; original.Position = 0; encrypted.Position = 0; Assert.AreNotEqual(original.ReadBytesToEnd(), encrypted.ReadBytesToEnd()); encrypted.Position = 0; secure = new SecureFile(encrypted, SecureFileMode.Decrypt, privateKey); secure.DecryptTo(decrypted); secure.Close(); secure = null; Assert.AreEqual(0, decrypted.Length); }
public void SecureFile_Stream_LargeContent() { string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedMemoryStream original = new EnhancedMemoryStream(); EnhancedMemoryStream encrypted = new EnhancedMemoryStream(); EnhancedMemoryStream decrypted = new EnhancedMemoryStream(); SecureFile secure = null; for (int i = 0; i < 128000; i++) { original.WriteByte((byte)i); } secure = new SecureFile(original, SecureFileMode.Encrypt, publicKey); original.Position = 0; secure.EncryptTo(encrypted, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; encrypted.Position = 0; secure = new SecureFile(encrypted, SecureFileMode.Decrypt, privateKey); secure.DecryptTo(decrypted); secure.Close(); secure = null; original.Position = 0; encrypted.Position = 0; CollectionAssert.AreNotEqual(original.ReadBytesToEnd(), encrypted.ReadBytesToEnd()); original.Position = 0; decrypted.Position = 0; CollectionAssert.AreEqual(original.ReadBytesToEnd(), decrypted.ReadBytesToEnd()); }
public void SecureFile_Stream_BadHash() { string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedMemoryStream original = new EnhancedMemoryStream(); EnhancedMemoryStream encrypted = new EnhancedMemoryStream(); EnhancedMemoryStream decrypted = new EnhancedMemoryStream(); SecureFile secure = null; byte b; for (int i = 0; i < 100; i++) { original.WriteByte((byte)i); } secure = new SecureFile(original, SecureFileMode.Encrypt, publicKey); original.Position = 0; secure.EncryptTo(encrypted, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; // Munge the last byte of the hash digest and then // confirm the this is detected encrypted.Position = encrypted.Length - 1; b = (byte)encrypted.ReadByte(); encrypted.Position = encrypted.Length - 1; encrypted.WriteByte((byte)(~b)); encrypted.Position = 0; secure = new SecureFile(encrypted, SecureFileMode.Decrypt, privateKey); try { secure.DecryptTo(decrypted); Assert.Fail("Corrupt hash digest not detected."); } catch { // Expecting an exception } }
public void SecureFile_Stream_Metadata() { string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedMemoryStream original = new EnhancedMemoryStream(); EnhancedMemoryStream encrypted = new EnhancedMemoryStream(); EnhancedMemoryStream decrypted = new EnhancedMemoryStream(); SecureFile secure = null; DateTime createTime = Helper.UtcNowRounded - TimeSpan.FromMinutes(1); DateTime writeTime = Helper.UtcNowRounded; secure = new SecureFile(original, SecureFileMode.Encrypt, publicKey); secure.Properties["Foo"] = "Bar"; secure.Properties["Hello"] = "World"; secure.FileName = "Test.dat"; secure.FullPath = "c:\\test\\test.dat"; secure.CreateTimeUtc = createTime; secure.WriteTimeUtc = writeTime; original.Position = 0; secure.EncryptTo(encrypted, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; original.Position = 0; encrypted.Position = 0; Assert.AreNotEqual(original.ReadBytesToEnd(), encrypted.ReadBytesToEnd()); encrypted.Position = 0; secure = new SecureFile(encrypted, SecureFileMode.Decrypt, privateKey); secure.DecryptTo(decrypted); Assert.AreEqual("Bar", secure.Properties["Foo"]); Assert.AreEqual("World", secure.Properties["Hello"]); Assert.AreEqual("Test.dat", secure.FileName); Assert.AreEqual("c:\\test\\test.dat", secure.FullPath); Assert.AreEqual(createTime, secure.CreateTimeUtc); Assert.AreEqual(writeTime, secure.WriteTimeUtc); secure.Close(); secure = null; Assert.AreEqual(0, decrypted.Length); }
/// <summary> /// Implements the background thread. /// </summary> private void DownloadThread() { DateTime lastWarningTime = DateTime.MinValue; PolledTimer pollTimer; bool resetTimer; try { // Initialize the GeoTracker file folder try { Helper.CreateFileTree(dataPath); if (File.Exists(downloadPath)) { SysLog.LogWarning("GeoTracker: Deleting existing temporary [{0}] file on startup.", downloadPath); Helper.DeleteFile(downloadPath); } if (File.Exists(decryptedPath)) { SysLog.LogWarning("GeoTracker: Deleting existing temporary [{0}] file on startup.", decryptedPath); Helper.DeleteFile(decryptedPath); } } catch (Exception e) { SysLog.LogException(e); } // Initalize the poll timer. We'll schedule an immediate download if the data file does // not exist, otherwise we'll delay the polling for a random period of time between // 0 and 15 minutes in the hope that we'll end up staggering the polling times across // the server cluster (so we won't hammer the source website). pollTimer = new PolledTimer(settings.IPGeocodeSourcePollInterval, false); resetTimer = false; if (!File.Exists(dataPath)) { pollTimer.FireNow(); } else { pollTimer.ResetRandomTemporary(TimeSpan.Zero, TimeSpan.FromMinutes(15)); } // The polling loop. while (true) { if (stopPending) { return; } try { if (pollDataNow) { pollTimer.FireNow(); pollDataNow = false; } if (pollTimer.HasFired) { DateTime fileDateUtc = DateTime.UtcNow; bool isUpdate = false; double fileSize = 0; ElapsedTimer downloadTimer; HttpWebRequest request; HttpWebResponse response; HttpStatusCode statusCode; resetTimer = true; // If a database file already exists then extract its last modify // date and use this in an If-Modified-Since request to the source // website to see if there's an updated file. if (File.Exists(dataPath)) { request = (HttpWebRequest)WebRequest.Create(settings.IPGeocodeSourceUri); request.Timeout = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; isUpdate = true; fileDateUtc = File.GetLastWriteTimeUtc(dataPath); request.Method = "HEAD"; request.IfModifiedSince = fileDateUtc; try { using (response = (HttpWebResponse)request.GetResponse()) statusCode = response.StatusCode; } catch (WebException e) { statusCode = ((HttpWebResponse)e.Response).StatusCode; } if (statusCode == HttpStatusCode.NotModified) { // The source website does not have an updated file. I'm going to // do one extra check to see if the file we have is more than 45 // days old and log a warning. Note that we're going to issue this // warning only once a week while the service is running. if (DateTime.UtcNow - fileDateUtc < TimeSpan.FromDays(45) || DateTime.UtcNow - lastWarningTime >= TimeSpan.FromDays(7)) { continue; } lastWarningTime = DateTime.UtcNow; const string warning = @"GeoTracker: The local copy of the MaxMind GeoIP City or GeoLite City database is [{0}] days old and should be updated. You may need to download a new copy of the database from http://maxmind.com, decompress it and upload it to the source website at [{1}]. Note: Make sure that the website is configured with the [.DAT=application/octet-stream] MIME mapping."; SysLog.LogWarning(warning, (int)(DateTime.UtcNow - fileDateUtc).TotalDays, settings.IPGeocodeSourceUri); continue; } } // Download the database to the temporary download file. Helper.DeleteFile(downloadPath); downloadTimer = new ElapsedTimer(true); fileSize = Helper.WebDownload(settings.IPGeocodeSourceUri, downloadPath, settings.IPGeocodeSourceTimeout, out response); downloadTimer.Stop(); // Set the file times to match the Last-Modified header received from the website (it any). string lastModified = response.Headers["Last-Modified"]; if (lastModified != null) { try { fileDateUtc = Helper.ParseInternetDate(lastModified); File.SetCreationTimeUtc(downloadPath, fileDateUtc); File.SetLastWriteTimeUtc(downloadPath, fileDateUtc); } catch (Exception e) { SysLog.LogException(e, "GeoTracker: Website for [{0}] returned invalid Last-Modified header [{1}].", settings.IPGeocodeSourceUri, lastModified); } } // Decrypt the file and set its file dates. var keyChain = new KeyChain(settings.IPGeocodeSourceRsaKey); using (var secureFile = new SecureFile(downloadPath, keyChain)) { secureFile.DecryptTo(decryptedPath); } File.SetCreationTimeUtc(decryptedPath, fileDateUtc); File.SetLastWriteTimeUtc(decryptedPath, fileDateUtc); // Verify the decrypted data file and then swap in new file. const string info = @"GeoTracker: {0} of IP-to-location database from [{1}] completed. Downloaded [{2:#.#}MB] bytes in [{3}]."; SysLog.LogInformation(info, isUpdate ? "Update download" : "Initial download", settings.IPGeocodeSourceUri, fileSize / (1024 * 1024), downloadTimer.ElapsedTime); // Create a new MaxMind lookup intance and then swap it in without interrupting // any queries in progress. try { LookupService newMaxMind; newMaxMind = new LookupService(decryptedPath, LookupService.GEOIP_MEMORY_CACHE); newMaxMind.close(); maxMind = newMaxMind; UpdateCount++; } catch (Exception e) { SysLog.LogException(e); SysLog.LogError("GeoTracker: The MaxMind downloaded database file [{0}] appears to be corrupted. This will be deleted so the downloader can get a fresh copy.", downloadPath); } lock (syncLock) { Helper.DeleteFile(dataPath); File.Copy(decryptedPath, dataPath); File.SetCreationTimeUtc(dataPath, fileDateUtc); File.SetLastWriteTimeUtc(dataPath, fileDateUtc); } // Delete the temporary files. Helper.DeleteFile(decryptedPath); Helper.DeleteFile(downloadPath); } } catch (WebException e) { SysLog.LogException(e); SysLog.LogWarning("GeoTracker: The download of the MaxMind database file has failed. The service will try again in 1 minute."); pollTimer.ResetTemporary(TimeSpan.FromMinutes(1)); resetTimer = false; } catch (ThreadAbortException e) { SysLog.LogException(e); throw; } catch (Exception e) { SysLog.LogException(e); } finally { if (resetTimer) { resetTimer = false; pollTimer.Reset(); } } Thread.Sleep(settings.BkInterval); } } finally { running = false; } }
public void SecureFile_File_KeyChain() { string encryptName = Path.GetTempFileName(); string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedMemoryStream original = new EnhancedMemoryStream(); SecureFile secure = null; try { for (int i = 0; i < 100; i++) { original.WriteByte((byte)i); } // Verify that SecureFile can find the correct private key in the key chain. secure = new SecureFile(original, SecureFileMode.Encrypt, publicKey); Assert.IsTrue(secure.SavePublicKey); Assert.AreEqual(publicKey, secure.PublicKey); original.Position = 0; secure.EncryptTo(encryptName, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; var keyChain = new KeyChain(); var decrypted = new EnhancedMemoryStream(); keyChain.Add(privateKey); secure = new SecureFile(encryptName, keyChain); secure.DecryptTo(decrypted); secure.Close(); secure = null; CollectionAssert.AreEqual(original.ToArray(), decrypted.ToArray()); // Verify that SecureFile throws a CryptographicException if the // key is not present in the chain. keyChain.Clear(); try { secure = new SecureFile(encryptName, keyChain); secure.DecryptTo(decrypted); Assert.Fail("Expecting a CryptographicException"); } catch (CryptographicException) { // Expecting this } finally { if (secure != null) { secure.Close(); secure = null; } } // Verify that SecureFile throws a CryptographicException if the // public key was not saved to the file. keyChain.Add(privateKey); secure = new SecureFile(original, SecureFileMode.Encrypt, publicKey); secure.SavePublicKey = false; original.Position = 0; secure.EncryptTo(encryptName, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; try { secure = new SecureFile(encryptName, keyChain); secure.DecryptTo(decrypted); Assert.Fail("Expecting a CryptographicException"); } catch (CryptographicException) { // Expecting this } finally { if (secure != null) { secure.Close(); secure = null; } } } finally { System.IO.File.Delete(encryptName); } }
public void SecureFile_File_Metadata() { string originalName = Path.GetTempFileName(); string encryptName = Path.GetTempFileName(); string decryptName = Path.GetTempFileName(); string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedStream original = null; EnhancedStream encrypted = null; EnhancedStream decrypted = null; SecureFile secure = null; DateTime createTime = Helper.UtcNowRounded - TimeSpan.FromMinutes(1); DateTime writeTime = Helper.UtcNowRounded; try { original = new EnhancedFileStream(originalName, FileMode.Create, FileAccess.ReadWrite); for (int i = 0; i < 100; i++) { original.WriteByte((byte)i); } original.Close(); original = null; Directory.SetCreationTimeUtc(originalName, createTime); Directory.SetLastWriteTimeUtc(originalName, writeTime); secure = new SecureFile(originalName, SecureFileMode.Encrypt, publicKey); secure.Properties["Foo"] = "Bar"; secure.Properties["Hello"] = "World"; secure.EncryptTo(encryptName, CryptoAlgorithm.AES, 256); Assert.AreEqual(Path.GetFileName(originalName), secure.FileName); Assert.AreEqual(createTime, secure.CreateTimeUtc); Assert.AreEqual(writeTime, secure.WriteTimeUtc); secure.Close(); secure = null; secure = new SecureFile(encryptName, SecureFileMode.Decrypt, privateKey); Assert.AreEqual("Bar", secure.Properties["Foo"]); Assert.AreEqual("World", secure.Properties["Hello"]); Assert.AreEqual(Path.GetFileName(originalName), secure.FileName); Assert.AreEqual(createTime, secure.CreateTimeUtc); Assert.AreEqual(writeTime, secure.WriteTimeUtc); secure.DecryptTo(decryptName); secure.Close(); secure = null; Assert.AreEqual(createTime, Directory.GetCreationTimeUtc(decryptName)); Assert.AreEqual(writeTime, Directory.GetLastWriteTimeUtc(decryptName)); original = new EnhancedFileStream(originalName, FileMode.Open, FileAccess.Read); encrypted = new EnhancedFileStream(encryptName, FileMode.Open, FileAccess.Read); decrypted = new EnhancedFileStream(decryptName, FileMode.Open, FileAccess.Read); original.Position = 0; encrypted.Position = 0; Assert.AreNotEqual(original.ReadBytesToEnd(), encrypted.ReadBytesToEnd()); original.Position = 0; decrypted.Position = 0; CollectionAssert.AreEqual(original.ReadBytesToEnd(), decrypted.ReadBytesToEnd()); } finally { if (original != null) { original.Close(); } if (encrypted != null) { encrypted.Close(); } if (decrypted != null) { decrypted.Close(); } System.IO.File.Delete(originalName); System.IO.File.Delete(encryptName); System.IO.File.Delete(decryptName); } }
public void SecureFile_File_BadHash() { string originalName = Path.GetTempFileName(); string encryptName = Path.GetTempFileName(); string decryptName = Path.GetTempFileName(); string privateKey = AsymmetricCrypto.CreatePrivateKey(CryptoAlgorithm.RSA, 1024); string publicKey = AsymmetricCrypto.GetPublicKey(CryptoAlgorithm.RSA, privateKey); EnhancedStream original = null; EnhancedStream encrypted = null; SecureFile secure = null; byte b; try { original = new EnhancedFileStream(originalName, FileMode.Create, FileAccess.ReadWrite); for (int i = 0; i < 100; i++) { original.WriteByte((byte)i); } original.Close(); original = null; secure = new SecureFile(originalName, SecureFileMode.Encrypt, publicKey); secure.EncryptTo(encryptName, CryptoAlgorithm.AES, 256); secure.Close(); secure = null; // Munge the last byte of the hash digest and then confirm // that the bad hash is detected. encrypted = new EnhancedFileStream(encryptName, FileMode.Open, FileAccess.ReadWrite); encrypted.Position = encrypted.Length - 1; b = (byte)encrypted.ReadByte(); encrypted.Position = encrypted.Length - 1; encrypted.WriteByte((byte)(~b)); encrypted.Close(); encrypted = null; ExtendedAssert.Throws <CryptographicException>( () => { secure = new SecureFile(encryptName, SecureFileMode.Decrypt, privateKey); secure.DecryptTo(decryptName); }); } finally { if (original != null) { original.Close(); } if (encrypted != null) { encrypted.Close(); } try { System.IO.File.Delete(originalName); } catch { } try { System.IO.File.Delete(encryptName); } catch { } try { System.IO.File.Delete(decryptName); } catch { } } }
private static int DecryptSecureFile(string[] args) { CommandLine cmdLine = new CommandLine(args, false); string inPath = cmdLine.GetOption("in", null); string outPath = cmdLine.GetOption("out", null); string keyChainOption = cmdLine.GetOption("keychain", null); KeyChain keyChain = null; if (inPath == null) { Program.Error("[-in:<path>] command line option is required."); return(1); } if (outPath == null) { Program.Error("[-out:<path>] command line option is required."); return(1); } if (keyChainOption != null) { string keyPath; int pos; SymmetricKey symkey; pos = keyChainOption.IndexOf(';'); if (pos != -1) { // Keychain file is encrypted. keyPath = keyChainOption.Substring(0, pos); symkey = new SymmetricKey(keyChainOption.Substring(pos + 1)); keyChain = new KeyChain(symkey, File.ReadAllBytes(keyPath)); } else { // Keychain file is not encrypted. keyChain = new KeyChain(); using (var input = new StreamReader(keyChainOption)) { for (var line = input.ReadLine(); line != null; line = input.ReadLine()) { var trimmed = line.Trim(); if (trimmed.Length == 0 || trimmed.StartsWith("//") || trimmed.StartsWith("--")) { continue; } keyChain.Add(trimmed); } } } if (keyChain.Count == 0) { Program.Error("The keychain is empty."); return(1); } } else { keyChain = new KeyChain(); } var keys = cmdLine.GetOptionValues("key"); foreach (var key in keys) { keyChain.Add(key); } if (keyChain.Count == 0) { Program.Error("A private RSA key must be specified using a [-key] or [-keychain] option."); return(1); } using (var secureFile = new SecureFile(inPath, keyChain)) { secureFile.DecryptTo(outPath); } return(0); }