Beispiel #1
0
        private string GetHash(string queryStringTokenKey)
        {
            if (!_memoryCache.TryGetValue(queryStringTokenKey, out var result))
            {
                using var entry = _memoryCache.CreateEntry(queryStringTokenKey);

                entry.SlidingExpiration = TimeSpan.FromHours(5);

                using var hmac = new HMACSHA256(_hashKey);

                // queryStringTokenKey also contains prefix
                var chars = queryStringTokenKey.AsSpan(TokenCacheKeyPrefix.Length);

                // only allocate on stack if it's small enough
                var requiredLength = Encoding.UTF8.GetByteCount(chars);
                var stringBytes    = requiredLength < 1024
                    ? stackalloc byte[requiredLength]
                    : new byte[requiredLength];

                // 256 for SHA-256, fits in stack nicely
                Span <byte> hashBytes = stackalloc byte[hmac.HashSize];

                var stringBytesLength = Encoding.UTF8.GetBytes(chars, stringBytes);

                hmac.TryComputeHash(stringBytes.Slice(0, stringBytesLength), hashBytes, out var hashBytesLength);

                entry.Value = result = Convert.ToBase64String(hashBytes.Slice(0, hashBytesLength));
            }

            return((string)result);
        }
Beispiel #2
0
        public static bool TryGetHmacSha256(byte[] secret, ReadOnlySpan <byte> message, Span <byte> sign)
        {
            secret.ThrowIfNullArgument(nameof(secret));
            var hmac = new HMACSHA256(secret);

            return(hmac.TryComputeHash(message, sign, out var written));
        }
        public static void ComputeHash(this HMACSHA256 hmac, ReadOnlySpan <byte> source, Span <byte> destination)
        {
            const int HashSize = 32;

            if (!hmac.TryComputeHash(source, destination, out int bytesWritten) || bytesWritten != HashSize)
            {
                throw new CryptographicUnexpectedOperationException();
            }
        }
Beispiel #4
0
        /// <summary>
        /// Decrypts a message using AES-256-CBC and verifies its HMAC-SHA-256 digest.
        /// </summary>
        /// <param name="ciphertext">A buffer containing an HMAC (32 byte), IV (16 byte) and at least one AES-encrypted block.</param>
        /// <param name="hmacKey">A 256 bit key for HMAC-SHA-256 message digest calculation.</param>
        /// <param name="aesKey">A 256 bit key for AES-256-CBC encryption.</param>
        public static ReadOnlyMemory <byte> DecryptWithHmac(ReadOnlyMemory <byte> ciphertext, byte[] hmacKey, byte[] aesKey)
        {
            if (hmacKey == null)
            {
                throw new ArgumentNullException(nameof(hmacKey));
            }
            if (aesKey == null)
            {
                throw new ArgumentNullException(nameof(aesKey));
            }
            if (ciphertext.Length < 64)
            {
                throw new ArgumentException("The ciphertext must be at least 64 bytes long.", nameof(ciphertext));
            }
            if (hmacKey.Length != 32)
            {
                throw new ArgumentException("The HMAC key must be exactly 32 bytes long.", nameof(hmacKey));
            }
            if (aesKey.Length != 32)
            {
                throw new ArgumentException("The AES key must be exaclty 32 bytes long.", nameof(aesKey));
            }

            using (var hmac = new HMACSHA256(hmacKey))
            {
                Span <byte> actualHash = stackalloc byte[32];
                if (!hmac.TryComputeHash(ciphertext.Span.Slice(32), actualHash, out _))
                {
                    throw new CryptographicException("HMAC calculation failed for unknown reason");
                }

                if (!actualHash.SequenceEqual(ciphertext.Span.Slice(0, 32)))
                {
                    throw new CryptographicException("Message Corrupted: The HMAC values are not equal. The encrypted block may be tampered.");
                }
            }

            byte[] iv = ciphertext.Slice(32, 16).ToArray();

            ReadOnlyMemory <byte> encrypted = ciphertext.Slice(48);

            if (!MemoryMarshal.TryGetArray(encrypted, out ArraySegment <byte> cipherSegment))
            {
                cipherSegment = encrypted.ToArray();
            }

            using ICryptoTransform transform = aes.CreateDecryptor(aesKey, iv);
            return(ProcessData(cipherSegment, transform, 0));
        }
Beispiel #5
0
        /// <summary>
        /// Encrypts a message with a random IV and AES-256-CBC and computes its HMAC-SHA-256 digest.
        /// The output order is HMAC (32 byte), IV (16 byte), ciphertext.
        /// </summary>
        /// <param name="plaintext"></param>
        /// <param name="hmacKey">A 256 bit key for HMAC-SHA-256 message digest calculation.</param>
        /// <param name="aesKey">A 256 bit key for AES-256-CBC encryption.</param>
        public static ReadOnlyMemory <byte> EncryptWithHmac(ReadOnlyMemory <byte> plaintext, byte[] hmacKey, byte[] aesKey)
        {
            if (hmacKey == null)
            {
                throw new ArgumentNullException(nameof(hmacKey));
            }
            if (aesKey == null)
            {
                throw new ArgumentNullException(nameof(aesKey));
            }
            if (hmacKey.Length != 32)
            {
                throw new ArgumentException("The HMAC key must be exactly 32 bytes long.", nameof(hmacKey));
            }
            if (aesKey.Length != 32)
            {
                throw new ArgumentException("The AES key must be exaclty 32 bytes long.", nameof(aesKey));
            }

            byte[] iv = new byte[16];
            RandomNumberGenerator.Fill(iv);
            Memory <byte> ciphertext;

            using (ICryptoTransform transform = aes.CreateEncryptor(aesKey, iv))
            {
                if (!MemoryMarshal.TryGetArray(plaintext, out ArraySegment <byte> plainSegment))
                {
                    plainSegment = plaintext.ToArray();
                }

                ciphertext = ProcessData(plainSegment, transform, 32 + 16);
            }

            iv.AsSpan().CopyTo(ciphertext.Span.Slice(32));

            using (var hmac = new HMACSHA256(hmacKey))
            {
                if (!hmac.TryComputeHash(ciphertext.Span.Slice(32), ciphertext.Span.Slice(0, 32), out _))
                {
                    throw new CryptographicException("HMAC calculation failed for unknown reason");
                }
            }

            return(ciphertext);
        }
Beispiel #6
0
 private bool TrySign(ReadOnlySpan <byte> input, Span <byte> output)
 {
     using (var hmac = new HMACSHA256(this.SigningKey))
         return(hmac.TryComputeHash(input, output, out _));
 }