private void GenerateRandomFileKey() { // BONUS: you can specify however many characters you want ;) var match = Regex.Match(cboKeySizes.Text, @"(?<charCount>[0-9]+)\s*characters\s*\((?<bitCount>[0-9]+)\s*bits\s*\)"); if (!match.Success) { ShowError("Specify a valid key size", "Invalid Key Size"); return; } var charSize = Int32.Parse(match.Groups["charCount"].Value); var bitSize = Int32.Parse(match.Groups["bitCount"].Value); // every char is 4 bits var charBitSize = charSize * 4; // If there is disagreement, pick the bigger one var maxBitSize = Math.Max(charBitSize, bitSize); txtMasterKey.Text = HexadecimalPasswordGenerator.GeneratePasswordOfBitSize(maxBitSize); }
private static void SplitFile() { // If your message is above 1K, you should use the hybrid approach of splitting the master secret // (which is the encryption key) and then use that to encrypt the file using OpenPGP and AES. // Here's an example of how to do this hybrid approach: // First, get a master secret of whatever size you want. 128 bits is plenty big and is a nice // balance of share sizes and security. However, to be fun, let's be super paranoid and go with // 256 bits (at the cost of bigger shares!) var masterPassword = HexadecimalPasswordGenerator.GeneratePasswordOfBitSize(bitSize: 256); var masterPasswordBytes = SecretEncoder.ParseHexString(masterPassword); // As mentioned above, the threshold is the total number of shares that need to come together // to unlock the secret. const int threshold = 3; // Now we create a class to help us encrypt everything else: var splitSecret = SecretSplitter.SplitFile(masterPasswordBytes, threshold); // We can generate as many shares as we'd like knowing that 3 of them need to come together // to reconstruct the secret. const int totalShares = 5; var shares = splitSecret.GetShares(totalShares); // The textual representation is what you'd typically distribute: var sharesText = shares.Select(s => s.ToString()).ToList(); // Remember that the shares are just mechanisms to distribute the master password. You still // need to distribute the encrypted file // Normally, you'd probably use the simpler method of // splitSecret.EncryptFile(inputPath, outputPath); // But this sample will use the more generic stream based version to keep everything in memory // Let's get a spot to store the encrypted output Stream encryptedStream; // First, let's make up a sample message of the numbers 1-1000 // As mentioned before, normally you'd just use a simple file name with EncryptFile or an // existing stream: // The OpenPGP format stores the name of the file, so we provide that here. It can be whatever // you want const string fileNameInsideEncryptedContainer = "numbers.txt"; using (var inputStream = new MemoryStream()) using (var streamWriter = new StreamWriter(inputStream)) { // Generate the numbers 1..1000 each on a single line for (int i = 1; i <= 1000; i++) { streamWriter.WriteLine(i); } // we're done writing streamWriter.Flush(); // Note that the size is bigger than 1250 bytes (the limit of splitting the message itself): Debug.Assert(inputStream.Length > 1250); // Now we can use the input. Reset its position to the start of the stream: inputStream.Position = 0; // Finally, encrypt the file. Save it with the filename metadata: encryptedStream = splitSecret.Encrypt(inputStream, fileNameInsideEncryptedContainer); } // We can save the contents of outputStream wherever we'd like. It's encrypted. // Let's go ahead and decrypt it. // First, take the threshold number of shares to recover the master secret: var combinedSecret = SecretCombiner.Combine(sharesText.Take(threshold)); // This metadata is present inside the encrypted file string decryptedFileName; DateTime decryptedFileDateTime; using (var decryptedStream = combinedSecret.Decrypt(encryptedStream, out decryptedFileName, out decryptedFileDateTime)) using (var decryptedStreamReader = new StreamReader(decryptedStream)) { // For fun, verify the decrypted file is what we expect: Debug.Assert(decryptedFileName == fileNameInsideEncryptedContainer); for (int expectedNumber = 1; expectedNumber <= 1000; expectedNumber++) { var currentLineText = decryptedStreamReader.ReadLine(); var currentLineNumber = Int32.Parse(currentLineText); Debug.Assert(currentLineNumber == expectedNumber); } // Nothing left: Debug.Assert(decryptedStreamReader.EndOfStream); } }