/// <remarks> /// This method demonstrates using a PackageFactory to extract a key. /// StreamMac to verify with a keyed HMAC, that tests the encrypted file before it is conditionally decrypted. /// If accepted the stream is then decrypted using the StreamCipher class. /// </remarks> private void Decrypt() { CipherDescription cipherDesc; KeyParams keyParam; byte[] extKey; try { using (FileStream inStream = new FileStream(_inputPath, FileMode.Open, FileAccess.Read)) { byte[] keyId = MessageHeader.GetKeyId(inStream); // get the keyheader and key material from the key file using (PackageFactory keyFactory = new PackageFactory(_keyFilePath, _container.Authority)) { if (keyFactory.AccessScope == KeyScope.NoAccess) { MessageBox.Show(keyFactory.LastError); return; } keyFactory.Extract(keyId, out cipherDesc, out keyParam, out extKey); } // offset start position is base header + Mac size int hdrOffset = MessageHeader.GetHeaderSize + cipherDesc.MacSize; // decrypt file extension and create a unique path _outputPath = Utilities.GetUniquePath(_outputPath + MessageHeader.GetExtension(inStream, extKey)); // if a signing key, test the mac: (MacSize = 0; not signed) if (cipherDesc.MacSize > 0) { // get the hmac for the encrypted file; this could be made selectable // via the KeyHeaderStruct MacDigest and MacSize members. using (StreamMac mstrm = new StreamMac(new SHA512HMAC(keyParam.IKM))) { // get the message header mac byte[] chksum = MessageHeader.GetMessageMac(inStream, cipherDesc.MacSize); // initialize mac stream inStream.Seek(hdrOffset, SeekOrigin.Begin); mstrm.Initialize(inStream); // get the mac; offset by header length + Mac and specify adjusted length byte[] hash = mstrm.ComputeMac(inStream.Length - hdrOffset, hdrOffset); // compare, notify and abort on failure if (!Compare.AreEqual(chksum, hash)) { MessageBox.Show("Message hash does not match! The file has been tampered with."); return; } } } // with this constructor, the StreamCipher class creates the cryptographic // engine using the description contained in the CipherDescription structure. // The (cipher and) engine are automatically destroyed in the cipherstream dispose using (StreamCipher cstrm = new StreamCipher(false, cipherDesc, keyParam)) { using (FileStream outStream = new FileStream(_outputPath, FileMode.Create, FileAccess.Write)) { // start at an input offset equal to the message header size inStream.Seek(hdrOffset, SeekOrigin.Begin); // use a percentage counter cstrm.ProgressPercent += new StreamCipher.ProgressDelegate(OnProgressPercent); // initialize internals cstrm.Initialize(inStream, outStream); // write the decrypted output to file cstrm.Write(); } } } // destroy the key keyParam.Dispose(); } catch (Exception ex) { if (File.Exists(_outputPath)) File.Delete(_outputPath); string message = ex.Message == null ? "" : ex.Message; MessageBox.Show("An error occured, the file could not be encrypted! " + message); } finally { Invoke(new MethodInvoker(() => { Reset(); })); } }
/// <summary> /// Demonstrates saving a key file using the PackageFactory class /// </summary> private void SaveKey() { try { // add the time/date expiration stamp if key policy is volatile if (HasPolicy(KeyPolicies.Volatile)) { if (dtVolatileTime.Value.Ticks > DateTime.Now.Ticks) _container.Authority.OptionFlag = dtVolatileTime.Value.Ticks; else throw new Exception("Invalid Expiry time. If a key is marked as Volatile, the expired time must be greater than the current time."); } // get the key tag description if (!string.IsNullOrEmpty(txtKeyDescription.Text)) { byte[] data = new byte[32]; byte[] tag = Encoding.ASCII.GetBytes(txtKeyDescription.Text); Array.Copy(tag, data, tag.Length < 32 ? tag.Length : 32); _container.Authority.PackageTag = data; } // get the number of subkeys to create in this package int keyCount = 1; if (!string.IsNullOrEmpty(txtSubKeyCount.Text) && txtSubKeyCount.Text != "0") int.TryParse(txtSubKeyCount.Text, out keyCount); // create a PackageKey; a key package can contain 1 or many thousands of 'subkeys'. Each subkey set // contains one group of unique random keying material; key, iv, and optional hmac key. // Each key set is used only once for encryption, guaranteeing that a unique set of values is used for every encryption cycle. PackageKey package = new PackageKey( _container.Authority, // the KeyAuthority structure _container.Description, // the CipherDescription structure keyCount, // the number of subkeys to add to this key package IdGenerator()); // the file extension encryption key // create and write the key using (PackageFactory factory = new PackageFactory(_keyFilePath, _container.Authority)) factory.Create(package); // store path _lastKeyPath = Path.GetDirectoryName(_keyFilePath); Reset(); lblStatus.Text = "The Key has been saved!"; } catch (Exception ex) { if (File.Exists(_keyFilePath)) File.Delete(_keyFilePath); string message = ex.Message == null ? "" : ex.Message; MessageBox.Show("An error occured, the key could not be created! " + message); } }
private void OnSelectKeyClick(object sender, EventArgs e) { string keyFile = GetFileOpenPath(KEY_DESC, KEY_FILT, _lastKeyPath); if (string.IsNullOrEmpty(keyFile)) return; // get the key policy flag long policies = 0; using (FileStream keyStream = new FileStream(keyFile, FileMode.Open, FileAccess.Read)) policies = PackageKey.GetKeyPolicy(keyStream); // test if key requires authentication if ((policies & (long)KeyPolicies.PackageAuth) == (long)KeyPolicies.PackageAuth) { // ask for passphrase if (ShowAuthDialog()) { using (PackageFactory keyFactory = new PackageFactory(keyFile, _container.Authority)) { if (keyFactory.AccessScope == KeyScope.NoAccess) { Array.Clear(_container.Authority.PackageId, 0, _container.Authority.PackageId.Length); MessageBox.Show("Passphrase does not match! This key requires authentication."); txtKeyFile.Text = KEY_DEFN; btnEncrypt.Enabled = false; return; } } } else { MessageBox.Show("Access denied! This key requires authentication."); txtKeyFile.Text = KEY_DEFN; btnEncrypt.Enabled = false; return; } } if (!_isEncryption) { if (IsMatchingKey(_inputPath, keyFile)) { _keyFilePath = keyFile; txtKeyFile.Text = keyFile; btnEncrypt.Enabled = true; btnInfo.Enabled = true; } else { _keyFilePath = string.Empty; txtKeyFile.Text = KEY_DEFN; btnEncrypt.Enabled = false; MessageBox.Show("Key does not match the message! Choose a different key.."); } } else { _keyFilePath = keyFile; txtKeyFile.Text = _keyFilePath; btnEncrypt.Enabled = true; btnInfo.Enabled = true; } if (!string.IsNullOrEmpty(_keyFilePath)) _lastKeyPath = Path.GetDirectoryName(_keyFilePath); }
private void OnInfoButtonClick(object sender, EventArgs e) { using (PackageFactory keyFactory = new PackageFactory(_keyFilePath, _container.Authority)) { using (FormInfo frmInfo = new FormInfo()) { frmInfo.SetInfo(keyFactory.KeyInfo()); frmInfo.ShowDialog(this); } } }
/// <summary> /// Compares keys between a message file and a key file /// </summary> private bool IsMatchingKey(string MessagePath, string KeyPath) { bool isEqual = false; if (File.Exists(MessagePath) && File.Exists(KeyPath)) { using (FileStream msgFile = new FileStream(MessagePath, FileMode.Open, FileAccess.Read)) { byte[] messageId = MessageHeader.GetKeyId(msgFile); using (PackageFactory factory = new PackageFactory(KeyPath, _container.Authority)) isEqual = factory.ContainsSubKey(messageId) > -1; } } return isEqual; }
/// <remarks> /// This method demonstrates using a PackageFactory and StreamCipher class to /// both encrypt a file, and optionally sign the message with an SHA512 HMAC. /// See the StreamCipher and StreamMac documentation for more examples. /// </remarks> private void Encrypt() { CipherDescription keyHeader; KeyParams keyParam; try { byte[] keyId = null; byte[] extKey = null; // get the keyheader and key material from the key file using (PackageFactory keyFactory = new PackageFactory(_keyFilePath, _container.Authority)) { // get the key info PackageInfo pki = keyFactory.KeyInfo(); if (!keyFactory.AccessScope.Equals(KeyScope.Creator)) { MessageBox.Show(keyFactory.LastError); return; } keyId = (byte[])keyFactory.NextKey(out keyHeader, out keyParam, out extKey).Clone(); } // offset start position is base header + Mac size int hdrOffset = MessageHeader.GetHeaderSize + keyHeader.MacSize; // with this constructor, the StreamCipher class creates the cryptographic // engine using the description in the CipherDescription. // The (cipher and) engine are destroyed in the cipherstream dispose using (StreamCipher cstrm = new StreamCipher(true, keyHeader, keyParam)) { using (FileStream inStream = new FileStream(_inputPath, FileMode.Open, FileAccess.Read)) { using (FileStream outStream = new FileStream(_outputPath, FileMode.Create, FileAccess.ReadWrite)) { // start at an output offset equal to the message header + MAC length outStream.Seek(hdrOffset, SeekOrigin.Begin); // use a percentage counter cstrm.ProgressPercent += new StreamCipher.ProgressDelegate(OnProgressPercent); // initialize internals cstrm.Initialize(inStream, outStream); // write the encrypted output to file cstrm.Write(); // write the key id to the header MessageHeader.SetKeyId(outStream, keyId); // write the encrypted file extension MessageHeader.SetExtension(outStream, MessageHeader.GetEncryptedExtension(Path.GetExtension(_inputPath), extKey)); // if this is a signing key, calculate the mac if (keyHeader.MacSize > 0) { // Get the mac for the encrypted file; Mac engine is SHA512 by default, // configurable via the CipherDescription MacSize and MacEngine members. // This is where you would select and initialize the correct Digest via the // CipherDescription members, and initialize the corresponding digest. // For expedience, this example is fixed on the default SHA512. // An optional progress event is available in the StreamMac class. using (StreamMac mstrm = new StreamMac(new SHA512HMAC(keyParam.IKM))) { // seek to end of header outStream.Seek(hdrOffset, SeekOrigin.Begin); // initialize mac stream mstrm.Initialize(outStream); // get the hash; specify offset and adjusted size byte[] hash = mstrm.ComputeMac(outStream.Length - hdrOffset, hdrOffset); // write the keyed hash value to the message header MessageHeader.SetMessageMac(outStream, hash); } } } } } // destroy the key keyParam.Dispose(); } catch (Exception ex) { if (File.Exists(_outputPath)) File.Delete(_outputPath); string message = ex.Message == null ? "" : ex.Message; MessageBox.Show("An error occured, the file could not be encrypted! " + message); } finally { Invoke(new MethodInvoker(() => { Reset(); })); } }
/// <summary> /// Creates a temporary PackageKey on disk, extracts and compares the copy /// <para>Throws an Exception on failure</</para> /// </summary> public static void PackageFactoryTest() { string path = GetTempPath(); KeyGenerator kgen = new KeyGenerator(); // populate a KeyAuthority structure KeyAuthority authority = new KeyAuthority(kgen.GetBytes(16), kgen.GetBytes(16), kgen.GetBytes(16), kgen.GetBytes(32), 0); // cipher paramaters CipherDescription desc = new CipherDescription( SymmetricEngines.RDX, 32, IVSizes.V128, CipherModes.CTR, PaddingModes.X923, BlockSizes.B128, RoundCounts.R14, Digests.Keccak512, 64, Digests.Keccak512); // create the package key PackageKey pkey = new PackageKey(authority, desc, 10); // write a key file using (PackageFactory pf = new PackageFactory(path, authority)) pf.Create(pkey); for (int i = 0; i < pkey.SubKeyCount; i++) { CipherDescription desc2; KeyParams kp1; KeyParams kp2; byte[] ext; byte[] id = pkey.SubKeyID[i]; // get at index using (FileStream stream = new FileStream(path, FileMode.Open)) kp2 = PackageKey.AtIndex(stream, i); // read the package from id using (PackageFactory pf = new PackageFactory(path, authority)) pf.Extract(id, out desc2, out kp1, out ext); // compare key material if (!Compare.AreEqual(kp1.Key, kp2.Key)) throw new Exception(); if (!Compare.AreEqual(kp1.IV, kp2.IV)) throw new Exception(); if (!Compare.AreEqual(pkey.ExtensionKey, ext)) throw new Exception(); if (!desc.Equals(desc2)) throw new Exception(); } if (File.Exists(path)) File.Delete(path); }