Example #1
0
        public KeyRing(IHostingEnvironment 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 var encryptionAlgorithm,
                    out var signingAlgorithm,
                    out var 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;
                }
            }
        }
Example #2
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 var encryptingAlgorithm, out var signingAlgorithm, out var 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.
            var 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.
            var 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);
            var 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.
            var 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));
        }
Example #3
0
        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.
            var algorithmIdentifierAsBytes = new byte[4];

            Buffer.BlockCopy(payload, 0, algorithmIdentifierAsBytes, 0, 4);
            var algorithmIdentifier = (ProtectorAlgorithm)(BitConverter.ToInt32(algorithmIdentifierAsBytes, 0));

            ProtectorAlgorithmHelper.GetAlgorithms(
                _defaultAlgorithm,
                out var encryptingAlgorithm,
                out var signingAlgorithm,
                out var keyDerivationIterationCount);

            // Now extract the signature
            var 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;
            var 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.
            var 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;
            var initializationVector = new byte[ivLength];
            var 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));
        }
Example #4
0
        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 var encryptingAlgorithm,
                out var signingAlgorithm,
                out var 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.
            var 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);
            var 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));
        }