Расшифровать AES 128 на T-SQL

#c# #.net #sql-server #tsql #encryption

#c# #.net #sql-сервер #tsql #шифрование

Вопрос:

У меня есть база данных, которая в настоящее время использует AES 128. База данных содержит около 8 миллионов записей, и клиент хочет вместо этого расшифровать пароли и хэшировать их, чтобы пароли не могли быть расшифрованы. Это веб-приложение с данными, хранящимися на удаленном сервере. Я пытался использовать веб-приложение для выполнения преобразования, но время ожидания истекает. Поскольку это 8 миллионов, потребуется некоторое время, чтобы просмотреть все элементы, поэтому моей следующей идеей было заставить SQL выполнять расшифровку и хеширование. Я мог бы позволить ему работать в течение следующих нескольких дней.

Проблема, с которой я сталкиваюсь, заключается в том, что каждый столбец содержит зашифрованный пароль с уникальной солью. Я не могу найти функцию для расшифровки пароля с использованием зашифрованного пароля и соли. Есть ли функция? Даже сторонний? Есть ли лучший способ сделать это?

Спасибо!

Ответ №1:

Самый простой / единственный способ сделать это в SQL Server — написать определяемую пользователем функцию CLR (UDF) на C #. Смотрите

для получения более подробной информации. Если бы это был я, я бы добавил новый столбец, содержащий хэш нового пароля, и периодически запускал инструкцию update для создания нового хэша пароля, что-то вроде этого:

 update top 10000 dbo.users
set hashedPassword = DecryptAndHash( encryptedPassword )
where hashedPassword is null
  

где DecryptAndHash() находится ваш UDF CLR. Как только преобразование будет завершено, вы должны быть свободны удалить старый столбец и развернуть обновление, чтобы использовать новую логику аутентификации.

Вероятно, вы хотите поместить триггер в таблицу, чтобы синхронизировать хэш с зашифрованным паролем на случай, если кто-нибудь изменит свой пароль, пока все это происходит.

ЧЕРТ возьми, код не должен быть намного сложнее, чем

 using System;
using Microsoft.SqlServer.Server;

namespace Sandbox
{
    public static class EncryptionFunctions
    {

        /// <summary>
        /// Encrypts a string
        /// </summary>
        /// <param name="plainText"></param>
        /// <returns>varbinary</returns>
        [SqlFunction]
        public static byte[] Encrypt( string plainText )
        {
            byte[] cipherText ;
            using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
            {
                cipherText = cipher.Encrypt( plainText ) ;
            }
            return cipherText ;
        }

        /// <summary>
        /// Decrypts a previously encrypted varbinary
        /// </summary>
        /// <param name="cipherText"></param>
        /// <returns>string</returns>
        [SqlFunction]
        public static string Decrypt( byte[] cipherText )
        {
            string plainText ;
            using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
            {
                plainText = cipher.Decrypt( cipherText ) ;
            }
            return plainText ;
        }

        /// <summary>
        /// Compute the secure hash of a [plaintext] string
        /// </summary>
        /// <param name="plainText"></param>
        /// <returns> varbinary </returns>
        [SqlFunction]
        public static byte[] SecureHash( string plainText )
        {
            byte[] hash ;
            using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
            {
                hash = cipher.ComputeSecureHash( plainText ) ;
            }
            return hash ;
        }

        /// <summary>
        /// Convenience wrapper method to take a previously encrypted string, decrypt it and compute its secure hash
        /// </summary>
        /// <param name="cipherText"></param>
        /// <returns>varbinary</returns>
        [SqlFunction]
        public static byte[] DecryptAndHash( byte[] cipherText )
        {
            byte[] hash ;
            using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
            {
                hash = cipher.ComputeSecureHash( cipher.Decrypt( cipherText ) ) ;
            }
            return hash ;
        }

        /// <summary>
        /// The core encrypt/decrypt/hash engine
        /// </summary>
        private class EncryptionEngine : IDisposable
        {
            /// <summary>
            /// get an instance of this class
            /// </summary>
            /// <returns></returns>
            public static EncryptionEngine GetInstance()
            {
                return new EncryptionEngine() ;
            }

            #region IDisposable Members

            /// <summary>
            /// Dispose of any unmanaged resources
            /// </summary>
            public void Dispose()
            {
                throw new NotImplementedException();
            }

            #endregion

            /// <summary>
            /// Encrypt a plaintext string
            /// </summary>
            /// <param name="plainText"></param>
            /// <returns></returns>
            internal byte[] Encrypt( string plainText )
            {
                throw new NotImplementedException();
            }

            /// <summary>
            /// Decrypt an encrypted string
            /// </summary>
            /// <param name="cipherText"></param>
            /// <returns></returns>
            internal string Decrypt( byte[] cipherText )
            {
                throw new NotImplementedException();
            }

            /// <summary>
            /// Compute the secure hash of a string
            /// </summary>
            /// <param name="plainText"></param>
            /// <returns></returns>
            internal byte[] ComputeSecureHash( string plainText )
            {
                throw new NotImplementedException();
            }

        }

    }
}
  

Реализация внутренних компонентов EncryptionEngine оставлена в качестве упражнения для читателя.

Комментарии:

1. Даже не знал, что они существуют. Я думаю, что это отличное решение. Спасибо!

2. Однако вам нужен SQL Server 2005 или 2008. Если вам не повезло работать с устаревшими версиями SQL Server, вы SOL.

Ответ №2:

Вы можете взглянуть на аутентификацию вашего приложения и увидеть из исходного кода, как оно проверяет подлинность пароля. Там вы должны увидеть, что приложение шифрует пароль и сравнивает его с зашифрованным значением в базе данных. Функцию шифрования там должно быть легко отменить. Соль обычно не используется вместе с шифрованием, она используется при генерации хэша для защиты от атак поиска.

Я не думаю, что SQL может выполнять расшифровку на AES128, во всяком случае, не простым способом. Но вы можете написать простой .NET app использует стандартные API, которые расшифровывают каждый пароль, хэшируют его с помощью соли и записывают обратно в базу данных.

Ответ №3:

Суть хранения зашифрованных паролей в том, что они не могут быть расшифрованы. Шифрование фактически выполняется на некоторой константе ( соль) с использованием пароля в качестве ключа.

Итак, в основном цель уже достигнута, вы не можете расшифровать «пароли», чтобы получить их текстовые версии.

Комментарии:

1. Зашифрованные пароли могут быть расшифрованы (если использовалось симметричное шифрование). Вы путаете хэшированный пароль с зашифрованным паролем.