public KeyRing(IWebHostEnvironment hostingEnvironment)
        {
            // Create the keyring directory if one doesn't exist.
            var keyRingDirectory = Path.Combine(hostingEnvironment.ContentRootPath, "keyring");

            Directory.CreateDirectory(keyRingDirectory);

            var directoryInfo = new DirectoryInfo(keyRingDirectory);

            if (directoryInfo.GetFiles("*.key").Length == 0)
            {
                ProtectorAlgorithmHelper.GetAlgorithms(
                    ProtectorAlgorithmHelper.DefaultAlgorithm,
                    out SymmetricAlgorithm encryptionAlgorithm,
                    out KeyedHashAlgorithm signingAlgorithm,
                    out int derivationCount);
                encryptionAlgorithm.GenerateKey();

                var keyAsString = Convert.ToBase64String(encryptionAlgorithm.Key);
                var keyId       = Guid.NewGuid().ToString();
                var keyFileName = Path.Combine(keyRingDirectory, keyId + ".key");
                using (var file = File.CreateText(keyFileName))
                {
                    file.WriteLine(keyAsString);
                }

                _keyDictionary.Add(keyId, keyAsString);

                CurrentKeyId = keyId;

                encryptionAlgorithm.Clear();
                encryptionAlgorithm.Dispose();
                signingAlgorithm.Dispose();
            }
            else
            {
                var filesOrdered = directoryInfo.EnumerateFiles()
                                   .OrderByDescending(d => d.CreationTime)
                                   .Select(d => d.Name)
                                   .ToList();

                foreach (var fileName in filesOrdered)
                {
                    var keyFileName = Path.Combine(keyRingDirectory, fileName);
                    var key         = File.ReadAllText(keyFileName);
                    var keyId       = Path.GetFileNameWithoutExtension(fileName);
                    _keyDictionary.Add(keyId, key);
                    CurrentKeyId = keyId;
                }
            }
        }
        public string Unprotect(string keyId, string data)
        {
            var masterKey = MasterKey(keyId);

            byte[] plainText;

            // Take our string and convert it back to bytes.
            var payload = Convert.FromBase64String(data);

            // Read the saved algorithm details and create instances of those algorithms.
            byte[] algorithmIdentifierAsBytes = new byte[4];
            Buffer.BlockCopy(payload, 0, algorithmIdentifierAsBytes, 0, 4);
            var algorithmIdentifier = (ProtectorAlgorithm)(BitConverter.ToInt32(algorithmIdentifierAsBytes, 0));

            ProtectorAlgorithmHelper.GetAlgorithms(
                _defaultAlgorithm,
                out SymmetricAlgorithm encryptingAlgorithm,
                out KeyedHashAlgorithm signingAlgorithm,
                out int keyDerivationIterationCount);

            // Now extract the signature
            byte[] signature = new byte[signingAlgorithm.HashSize / 8];
            Buffer.BlockCopy(payload, 4, signature, 0, signingAlgorithm.HashSize / 8);

            // And finally grab the rest of the data
            var dataLength = payload.Length - 4 - signature.Length;

            byte[] cipherTextAndIV = new byte[dataLength];
            Buffer.BlockCopy(payload, 4 + signature.Length, cipherTextAndIV, 0, dataLength);

            // Check the signature before anything else is done to detect tampering and avoid
            // oracles.
            byte[] computedSignature = SignData(
                cipherTextAndIV,
                masterKey,
                encryptingAlgorithm,
                signingAlgorithm,
                keyDerivationIterationCount);
            if (!ByteArraysEqual(computedSignature, signature))
            {
                throw new CryptographicException(@"Invalid Signature.");
            }
            signingAlgorithm.Clear();
            signingAlgorithm.Dispose();

            // The signature is valid, so now we can work on decrypting the data.
            var ivLength = encryptingAlgorithm.BlockSize / 8;

            byte[] initializationVector = new byte[ivLength];
            byte[] cipherText           = new byte[cipherTextAndIV.Length - ivLength];
            // The IV is embedded in the cipher text, so we extract it out.
            Buffer.BlockCopy(cipherTextAndIV, 0, initializationVector, 0, ivLength);
            // Then we get the encrypted data.
            Buffer.BlockCopy(cipherTextAndIV, ivLength, cipherText, 0, cipherTextAndIV.Length - ivLength);

            encryptingAlgorithm.Key = DerivedEncryptionKey(
                masterKey,
                encryptingAlgorithm,
                keyDerivationIterationCount);
            encryptingAlgorithm.IV = initializationVector;

            // Decrypt
            using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(ms, encryptingAlgorithm.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherText);
                    cs.FlushFinalBlock();
                    plainText = ms.ToArray();
                }
            encryptingAlgorithm.Clear();
            encryptingAlgorithm.Dispose();

            // And convert from the bytes back to a string.
            return(Encoding.UTF8.GetString(plainText));
        }
        public string Protect(string keyId, string data)
        {
            // Get the default algorithms.
            // We does this so we can embed the algorithm details used in the cipher text so we can
            // change algorithms as yet another collision appears in a hashing algorithm.
            // See https://media.blackhat.com/bh-us-10/whitepapers/Sullivan/BlackHat-USA-2010-Sullivan-Cryptographic-Agility-wp.pdf
            ProtectorAlgorithmHelper.GetAlgorithms(
                _defaultAlgorithm,
                out SymmetricAlgorithm encryptingAlgorithm,
                out KeyedHashAlgorithm signingAlgorithm,
                out int keyDerivationIterationCount);

            var masterKey = MasterKey(keyId);

            // Convert the string to bytes, because encryption works on bytes, not strings.
            var plainText = Encoding.UTF8.GetBytes(data);

            byte[] cipherTextAndIV;

            // Derive a key for encryption from the master key
            encryptingAlgorithm.Key = DerivedEncryptionKey(
                masterKey,
                encryptingAlgorithm,
                keyDerivationIterationCount);

            // As we need this to be deterministic, we need to force an IV that is derived from the plain text.
            encryptingAlgorithm.IV = DerivedInitializationVector(
                masterKey,
                data,
                encryptingAlgorithm,
                keyDerivationIterationCount);

            // And encrypt
            using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(
                           ms,
                           encryptingAlgorithm.CreateEncryptor(),
                           CryptoStreamMode.Write))
                {
                    cs.Write(plainText);
                    cs.FlushFinalBlock();
                    var encryptedData = ms.ToArray();

                    cipherTextAndIV = CombineByteArrays(encryptingAlgorithm.IV, encryptedData);
                }

            // Now get a signature for the data so we can detect tampering in situ.
            byte[] signature = SignData(
                cipherTextAndIV,
                masterKey,
                encryptingAlgorithm,
                signingAlgorithm,
                keyDerivationIterationCount);

            // Add the signature to the cipher text.
            var signedData = CombineByteArrays(signature, cipherTextAndIV);

            // Add our algorithm identifier to the combined signature and cipher text.
            var algorithmIdentifier = BitConverter.GetBytes((int)_defaultAlgorithm);

            byte[] output = CombineByteArrays(algorithmIdentifier, signedData);

            // Clean everything up.
            encryptingAlgorithm.Clear();
            signingAlgorithm.Clear();
            encryptingAlgorithm.Dispose();
            signingAlgorithm.Dispose();

            Array.Clear(plainText, 0, plainText.Length);

            // Return the results as a string.
            return(Convert.ToBase64String(output));
        }
Example #4
0
        public string Protect(string data)
        {
            // Get the default algorithms.
            // We does this so we can embed the algorithm details used in the cipher text so we can
            // change algorithms as yet another collision appears in a hashing algorithm.
            // See https://media.blackhat.com/bh-us-10/whitepapers/Sullivan/BlackHat-USA-2010-Sullivan-Cryptographic-Agility-wp.pdf
            ProtectorAlgorithmHelper.GetAlgorithms(
                _defaultAlgorithm,
                out SymmetricAlgorithm encryptingAlgorithm,
                out KeyedHashAlgorithm signingAlgorithm,
                out int keyDerivationIterationCount);

            // Use the newest key from the keyring.
            // We know this is a guid, because that's how our keyring works underneath,
            // so we can prepend this later to the result.
            string keyId     = _keyRing.CurrentKeyId;
            var    masterKey = Key(keyId);

            // Convert the string to bytes, because encryption works on bytes, not strings.
            var plainText = Encoding.UTF8.GetBytes(data);

            byte[] cipherTextAndIV;

            // Derive a key for encryption from the master key
            encryptingAlgorithm.Key = DerivedEncryptionKey(
                masterKey,
                encryptingAlgorithm,
                keyDerivationIterationCount);

            // When the underlying encryption class is created it has a random IV by default
            // So we don't need to do anything IV wise.

            // And encrypt
            using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(
                           ms,
                           encryptingAlgorithm.CreateEncryptor(),
                           CryptoStreamMode.Write))
                {
                    cs.Write(plainText);
                    cs.FlushFinalBlock();
                    var encryptedData = ms.ToArray();

                    cipherTextAndIV = CombineByteArrays(encryptingAlgorithm.IV, encryptedData);
                }

            // Now get a signature for the data so we can detect tampering in situ.
            byte[] signature = SignData(
                cipherTextAndIV,
                masterKey,
                encryptingAlgorithm,
                signingAlgorithm,
                keyDerivationIterationCount);

            // Add the signature to the cipher text.
            var signedData = CombineByteArrays(signature, cipherTextAndIV);

            // Add our algorithm identifier to the combined signature and cipher text.
            var algorithmIdentifier = BitConverter.GetBytes((int)_defaultAlgorithm);

            byte[] dataPlusAlgorithmId = CombineByteArrays(algorithmIdentifier, signedData);

            // Now we need to put our key identifier in. In our implementation this is a GUID
            // so let's convert it back to one, then turn it to bytes.
            byte[] output = CombineByteArrays(Guid.Parse(keyId).ToByteArray(), dataPlusAlgorithmId);

            // Clean everything up.
            encryptingAlgorithm.Clear();
            signingAlgorithm.Clear();
            encryptingAlgorithm.Dispose();
            signingAlgorithm.Dispose();

            Array.Clear(plainText, 0, plainText.Length);

            // Return the results as a string.
            return(Convert.ToBase64String(output));
        }