F.34. pgcrypto — криптографические функции#
F.34. pgcrypto — криптографические функции #
Модуль pgcrypto
предоставляет криптографические функции для Tantor BE.
Этот модуль считается "доверенным", то есть его можно установить
недоступным пользователям, у которых есть привилегия CREATE
в текущей базе данных.
pgcrypto
требует наличия OpenSSL и не будет установлено, если поддержка OpenSSL не была выбрана при сборке PostgreSQL.
F.34.1. Общие функции хеширования #
F.34.1.1. digest()
#
digest(data text, type text) returns bytea digest(data bytea, type text) returns bytea
Вычисляет двоичный хеш заданных данных data
.
type
- это алгоритм, который будет использоваться.
Стандартные алгоритмы: md5
, sha1
,
sha224
, sha256
,
sha384
и sha512
.
Кроме того, автоматически выбирается любой алгоритм хеширования, поддерживаемый OpenSSL.
Если нужно получить дайджест в виде шестнадцатеричной строки, используйте функцию encode()
для полученного результата. Например:
CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$ SELECT encode(digest($1, 'sha1'), 'hex') $$ LANGUAGE SQL STRICT IMMUTABLE;
F.34.1.2. hmac()
#
hmac(data text, key text, type text) returns bytea hmac(data bytea, key bytea, type text) returns bytea
Вычисляет хешированный MAC для data
с ключом key
.
type
такой же, как в digest()
.
Это похоже на функцию digest()
, но хеш можно пересчитать только зная ключ. Это предотвращает ситуацию, когда кто-то изменяет данные и также изменяет хеш, чтобы он совпадал.
Если ключ больше размера блока хеша, он будет сначала хеширован, а затем результат будет использоваться в качестве ключа.
F.34.2. Функции хеширования паролей #
Функции crypt()
и gen_salt()
специально разработаны для хеширования паролей. crypt()
выполняет хеширование, а gen_salt()
подготавливает параметры алгоритма для него.
Алгоритмы в функции crypt()
отличаются от обычных алгоритмов хеширования MD5 или SHA1 следующими особенностями:
Они медленные. Поскольку объем данных настолько мал, это единственный способ сделать подбор паролей сложным.
Они используют случайное значение, называемое соль, чтобы пользователи с одинаковым паролем имели разные зашифрованные пароли. Это также дополнительная защита от обратного преобразования алгоритма.
Они включают тип алгоритма в результат, поэтому пароли, хешированные с использованием разных алгоритмов, могут сосуществовать.
Некоторые из них адаптивные — это означает, что при увеличении скорости компьютеров вы можете настроить алгоритм на более медленную работу, не внося несовместимости с существующими паролями.
Таблица F.18 перечисляет алгоритмы, поддерживаемые функцией crypt()
.
Таблица F.18. Поддерживаемые алгоритмы для функции crypt()
Алгоритм | Максимальная длина пароля | Адаптивный? | Биты соли | Длина вывода | Описание |
---|---|---|---|---|---|
bf | 72 | yes | 128 | 60 | Основанный на Blowfish, вариант 2a |
md5 | неограниченный | нет | 48 | 34 | криптография на основе MD5 |
xdes | 8 | yes | 24 | 20 | Расширенный DES |
des | 8 | no | 12 | 13 | Оригинальное UNIX-шифрование |
F.34.2.1. crypt()
#
crypt(password text, salt text) returns text
Вычисляет хеш в стиле crypt(3) для password
.
При сохранении нового пароля необходимо использовать функцию gen_salt()
для генерации нового значения salt
.
Для проверки пароля передайте сохраненное значение хеша в качестве salt
и проверьте, совпадает ли результат с сохраненным значением.
Пример установки нового пароля:
UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));
Пример аутентификации:
SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ;
Это возвращает true
, если введенный пароль правильный.
F.34.2.2. gen_salt()
#
gen_salt(type text [, iter_count integer ]) returns text
Генерирует новую случайную строку соли для использования в функции crypt()
.
Строка соли также сообщает функции crypt()
, какой алгоритм использовать.
Параметр type
определяет алгоритм хеширования.
Принимаемые типы: des
, xdes
,
md5
и bf
.
Параметр iter_count
позволяет пользователю указать количество итераций для алгоритмов, которые его используют.
Чем выше значение, тем больше времени требуется для хеширования пароля и, следовательно, больше времени для его взлома. Однако слишком большое значение может привести к тому, что время вычисления хеша составит несколько лет, что является несколько неудобным. Если параметр iter_count
не указан, используется значение итераций по умолчанию.
Допустимые значения для параметра iter_count
зависят от алгоритма и указаны в таблице Таблица F.19.
Таблица F.19. Количество итераций для crypt()
Алгоритм | По умолчанию | Минимум | Максимум |
---|---|---|---|
xdes | 725 | 1 | 16777215 |
bf | 6 | 4 | 31 |
Для xdes
существует дополнительное ограничение: количество итераций должно быть нечетным числом.
Для выбора подходящего количества итераций рассмотрите, что исходный DES crypt был разработан для обеспечения скорости выполнения 4 хешей в секунду на аппаратуре того времени. Скорость медленнее 4 хешей в секунду, вероятно, снизит удобство использования. Скорость быстрее 100 хешей в секунду, вероятно, слишком высока.
Таблица F.20 дает обзор относительной медлительности
различных алгоритмов хеширования.
Таблица показывает, сколько времени потребуется для попытки всех
комбинаций символов в пароле из 8 символов, предполагая,
что пароль содержит либо только строчные буквы, либо
строчные и заглавные буквы и цифры.
В записях crypt-bf
, число после слэша -
параметр iter_count
функции
gen_salt
.
Таблица F.20. Скорости хеш-алгоритмов
Алгоритм | Хеши/сек | Для [a-z] | Для [A-Za-z0-9] | Продолжительность относительно md5 hash |
---|---|---|---|---|
crypt-bf/8 | 1792 | 4 года | 3927 лет | 100k |
crypt-bf/7 | 3648 | 2 года | 1929 года | 50k |
crypt-bf/6 | 7168 | 1 год | 982 года | 25k |
crypt-bf/5 | 13504 | 188 дней | 521 лет | 12.5k |
crypt-md5 | 171584 | 15 дней | 41 год | 1к |
crypt-des | 23221568 | 157.5 минуты | 108 дней | 7 |
sha1 | 37774272 | 90 минут | 68 дней | 4 |
md5 (хеш) | 150085504 | 22.5 минуты | 17 дней | 1 |
Примечания:
Используемая машина - Intel Mobile Core i3.
crypt-des
иcrypt-md5
номера алгоритмов взяты из вывода John the Ripper v1.6.38-test
.md5 hash
числа взяты из mdcrack 1.2.sha1
числа взяты из lcrack-20031130-beta.crypt-bf
числа взяты с использованием простой программы, которая выполняет цикл из 1000 8-символьных паролей. Таким образом, можно показать скорость при различных количествах итераций. Для справки:john -test
показывает 13506 циклов/сек дляcrypt-bf/5
. (Очень небольшая разница в результатах соответствует тому факту, что реализацияcrypt-bf
вpgcrypto
такая же, как используется в John the Ripper.)
Обратите внимание, что “попробовать все комбинации” не является реалистичным упражнением. Обычно взлом паролей выполняется с помощью словарей, которые содержат как обычные слова, так и различные их мутации. Таким образом, даже пароли, напоминающие слова, могут быть взломаны намного быстрее, чем указанные выше числа, в то время как 6-символьный пароль, не похожий на слово, может остаться невзломанным. Или нет.
F.34.3. Функции шифрования PGP #
Функции здесь реализуют часть шифрования OpenPGP (RFC 4880) стандарта. Поддерживаются как симметричное, так и асимметричное шифрование.
Зашифрованное сообщение PGP состоит из 2 частей, или пакетов:
Пакет, содержащий ключ сессии — либо симметрично-ключевой, либо зашифрованный с использованием открытого ключа.
Пакет, содержащий данные, зашифрованные сессионным ключом.
При шифровании с использованием симметричного ключа (т.е. пароля):
Предоставленный пароль хешируется с использованием алгоритма String2Key (S2K). Это довольно похоже на алгоритмы
crypt()
- намеренно медленные и с случайной солью - но оно создает полноценный двоичный ключ.Если запрашивается отдельный ключ сессии, будет сгенерирован новый случайный ключ. В противном случае ключ S2K будет использоваться напрямую в качестве ключа сессии.
Если ключ S2K должен использоваться напрямую, то в пакет ключа сессии будут включены только настройки S2K. В противном случае ключ сессии будет зашифрован с помощью ключа S2K и помещен в пакет ключа сессии.
При шифровании с помощью открытого ключа:
Генерируется новый случайный ключ сессии.
Он шифруется с использованием открытого ключа и помещается в пакет сессионного ключа.
В любом случае данные, которые должны быть зашифрованы, обрабатываются следующим образом:
Дополнительная обработка данных: сжатие, преобразование в UTF-8 и/или преобразование символов конца строки.
Данные предваряются блоком случайных байтов. Это эквивалентно использованию случайного инициализационного вектора (IV).
В конец добавляется SHA1-хеш случайного префикса и данных.
Весь этот текст зашифрован сессионным ключом и помещен в пакет данных.
F.34.3.1. pgp_sym_encrypt()
#
pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
Зашифруйте data
симметричным PGP ключом psw
.
Параметр options
может содержать настройки параметров,
как описано ниже.
F.34.3.2. pgp_sym_decrypt()
#
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
Расшифровать симметрично-зашифрованное PGP-сообщение.
Расшифровка данных типа bytea
с использованием функции pgp_sym_decrypt
запрещена.
Это сделано для предотвращения вывода недопустимых символьных данных. Расшифровка исходно текстовых данных с использованием функции pgp_sym_decrypt_bytea
допустима.
Параметр options
может содержать настройки опций,
как описано ниже.
F.34.3.3. pgp_pub_encrypt()
#
pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
Шифрование data
с использованием открытого PGP-ключа key
.
Если передать этой функции секретный ключ, будет сгенерирована ошибка.
Параметр options
может содержать настройки опций,
как описано ниже.
F.34.3.4. pgp_pub_decrypt()
#
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea
Расшифровать сообщение, зашифрованное с использованием открытого ключа. Параметр key
должен быть секретным ключом, соответствующим открытому ключу, использованному для шифрования. Если секретный ключ защищен паролем, вы должны указать пароль в psw
. Если пароля нет, но нужно указать параметры, вам нужно указать пустой пароль.
Расшифровка данных типа bytea
с использованием функции pgp_pub_decrypt
запрещена.
Это сделано для предотвращения вывода недопустимых символьных данных. Расшифровка исходно текстовых данных с использованием функции pgp_pub_decrypt_bytea
допустима.
Параметр options
может содержать настройки опций,
как описано ниже.
F.34.3.5. pgp_key_id()
#
pgp_key_id(bytea) returns text
pgp_key_id
извлекает идентификатор ключа открытого или секретного ключа PGP.
Или он дает идентификатор ключа, который был использован для шифрования данных, если дано
зашифрованное сообщение.
Он может возвращать 2 специальных идентификатора ключей:
SYMKEY
Сообщение зашифровано симметричным ключом.
ANYKEY
Сообщение зашифровано с использованием открытого ключа, но идентификатор ключа был удален. Это означает, что вам нужно попробовать все ваши секретные ключи, чтобы узнать, какой из них расшифровывает его. Сам
pgcrypto
не генерирует такие сообщения.
Обратите внимание, что разным ключам может быть присвоен один и тот же идентификатор. Это редкое, но нормальное событие. Клиентское приложение должно попытаться расшифровать каждый ключ, чтобы узнать, какой подходит - подобно обработке ANYKEY
.
F.34.3.6. armor()
, dearmor()
#
armor(data bytea [ , keys text[], values text[] ]) returns text dearmor(data text) returns bytea
Эти функции оборачивают/распаковывают двоичные данные в формат PGP ASCII-armor, который в основном представляет собой Base64 с контрольной суммой CRC и дополнительным форматированием.
Если указаны массивы keys
и values
,
к каждой паре ключ/значение в зашифрованном формате добавляется заголовок брони.
Оба массива должны быть одномерными и иметь одинаковую длину.
Ключи и значения не могут содержать не-ASCII символы.
F.34.3.7. pgp_armor_headers
#
pgp_armor_headers(data text, key out text, value out text) returns setof record
pgp_armor_headers()
извлекает заголовки брони из
data
. Возвращаемое значение - набор строк с двумя столбцами,
ключ и значение. Если ключи или значения содержат любые не-ASCII символы,
они рассматриваются как UTF-8.
F.34.3.8. Опции для функций PGP #
Опции названы так, чтобы быть похожими на GnuPG. Значение опции должно быть указано после знака равенства; разделяйте опции друг от друга запятыми. Например:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
Все параметры, кроме convert-crlf
, применяются только к функциям шифрования. Функции дешифрования получают параметры из данных PGP.
Самые интересные параметры, вероятно, это compress-algo
и unicode-mode
.
Остальные должны иметь разумные значения по умолчанию.
F.34.3.8.1. cipher-algo #
Какой алгоритм шифрования использовать.
Значения: bf, aes128, aes192, aes256, 3des, cast5
По умолчанию: aes128
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.2. compress-algo #
Какой алгоритм сжатия использовать. Доступен только если Tantor BE был собран с использованием zlib.
Значения:
0 - без сжатия
1 - сжатие ZIP
2 - сжатие ZLIB (= ZIP плюс метаданные и блочные CRC)
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.3. compress-level #
На сколько сжимать. Более высокие уровни сжимают меньше, но работают медленнее. 0 отключает сжатие.
Значения: 0, 1-9
По умолчанию: 6
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.4. convert-crlf #
Определяет, нужно ли преобразовывать \n
в \r\n
при шифровании и \r\n
в \n
при дешифровании. Согласно RFC 4880, текстовые данные должны храниться с использованием перевода строки \r\n
. Используйте это, чтобы получить полностью совместимое с RFC поведение.
Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt
F.34.3.8.5. disable-mdc #
Не защищайте данные с помощью SHA-1. Единственная хорошая причина использовать эту опцию - достижение совместимости с древними продуктами PGP, предшествующими добавлению защищенных пакетов SHA-1 в RFC 4880. Недавнее программное обеспечение gnupg.org и pgp.com отлично поддерживает его.
Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.6. sess-key #
Используйте отдельный ключ сессии. Шифрование с открытым ключом всегда использует отдельный ключ сессии; эта опция предназначена для симметричного шифрования, которое по умолчанию использует ключ S2K напрямую.
Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt
F.34.3.8.7. s2k-mode #
Какой алгоритм S2K использовать.
Значения:
0 - Без соли. Опасно!
1 - С солью, но с фиксированным количеством итераций.
3 - Переменное количество итераций.
По умолчанию: 3
Применяется к: pgp_sym_encrypt
F.34.3.8.8. счетчик-с2к #
Количество итераций алгоритма S2K, которое будет использоваться. Оно должно быть значением от 1024 до 65011712 включительно.
По умолчанию: случайное значение между 65536 и 253952
Применяется к: pgp_sym_encrypt, только с s2k-mode=3
F.34.3.8.9. s2k-digest-algo #
Какой алгоритм хеширования использовать при вычислении S2K.
Значения: md5, sha1
По умолчанию: sha1
Применяется к: pgp_sym_encrypt
F.34.3.8.10. s2k-cipher-algo #
Какой шифр использовать для шифрования отдельного ключа сессии.
Значения: bf, aes, aes128, aes192, aes256
По умолчанию: используйте cipher-algo
Применяется к: pgp_sym_encrypt
F.34.3.8.11. unicode-mode #
Определяет, нужно ли преобразовывать текстовые данные из внутренней кодировки базы данных в UTF-8 и обратно. Если ваша база данных уже использует UTF-8, преобразование не будет выполняться, но сообщение будет помечено как UTF-8. Без этой опции это не произойдет.
Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.9. Генерация PGP-ключей с помощью GnuPG #
Для генерации нового ключа:
gpg --gen-key
Предпочтительный тип ключа - “DSA и Elgamal”.
Для шифрования RSA необходимо создать ключ DSA или RSA только для подписи в качестве основного, а затем добавить подключ RSA для шифрования с помощью gpg --edit-key
.
Для перечисления ключей:
gpg --list-secret-keys
Для экспорта открытого ключа в формате ASCII-armor:
gpg -a --export KEYID > public.key
Чтобы экспортировать секретный ключ в формате ASCII-armor:
gpg -a --export-secret-keys KEYID > secret.key
Вам нужно использовать dearmor()
для этих ключей перед передачей их функциям PGP. Или, если вы можете работать с двоичными данными, вы можете опустить -a
из команды.
Для получения более подробной информации см. man gpg
,
Руководство по
безопасности GNU и другую документацию на
https://www.gnupg.org/.
F.34.3.10. Ограничения кода PGP #
Нет поддержки подписи. Это также означает, что не проверяется, принадлежит ли шифровальный подключ к основному ключу.
Нет поддержки ключа шифрования в качестве главного ключа. Поскольку такая практика обычно не рекомендуется, это не должно быть проблемой.
Нет поддержки нескольких подключей. Это может показаться проблемой, так как это общая практика. С другой стороны, вы не должны использовать свои обычные GPG/PGP ключи с
pgcrypto
, а должны создать новые, так как сценарий использования здесь совершенно иной.
F.34.4. Функции сырого шифрования #
Эти функции только выполняют шифрование данных; они не имеют никаких продвинутых функций шифрования PGP. Поэтому у них есть некоторые серьезные проблемы:
Они используют ключ пользователя напрямую в качестве ключа шифрования.
Они не предоставляют никакой проверки целостности, чтобы увидеть, были ли изменены зашифрованные данные.
Они ожидают, что пользователи будут сами управлять всеми параметрами шифрования, включая вектор инициализации (IV).
Они не обрабатывают текст.
Таким образом, с введением шифрования PGP рекомендуется избегать использования функций сырого шифрования.
encrypt(data bytea, key bytea, type text) returns bytea decrypt(data bytea, key bytea, type text) returns bytea encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
Шифрование/дешифрование данных с использованием метода шифрования, указанного параметром type
. Синтаксис строки параметра type
следующий:
algorithm
[-
mode
] [/pad:
padding
]
где algorithm
является одним из:
bf
— Blowfishaes
— AES (Rijndael-128, -192 или -256)
и mode
является одним из:
cbc
— следующий блок зависит от предыдущего (по умолчанию)ecb
— каждый блок шифруется отдельно (только для тестирования)
и padding
является одним из:
pkcs
— данные могут иметь любую длину (по умолчанию)none
— данные должны быть кратны размеру блока шифрования
Так, например, эти два выражения эквивалентны:
encrypt(data, 'fooz', 'bf') encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
В функциях encrypt_iv
и decrypt_iv
параметр iv
является начальным значением для режима CBC; в режиме ECB он игнорируется. Если значение не соответствует размеру блока, оно обрезается или дополняется нулями. По умолчанию в функциях без этого параметра используется значение всех нулей.
F.34.5. Функции для генерации случайных данных #
gen_random_bytes(count integer) returns bytea
Возвращает криптографически надежные случайные байты count
.
За один раз можно извлечь не более 1024 байт. Это сделано для предотвращения
истощения пула генератора случайных чисел.
gen_random_uuid() returns uuid
Возвращает версию 4 (случайный) UUID. (Устарело, эта функция внутренне вызывает ядреную функцию того же имени).
F.34.6. Примечания #
F.34.6.1. Конфигурация #
pgcrypto
настраивается в соответствии с результатами выполнения основного сценария configure
в PostgreSQL. Опции, которые на него влияют, - это --with-zlib
и --with-ssl=openssl
.
Когда компилируется с zlib, функции шифрования PGP могут сжимать данные перед шифрованием.
pgcrypto
требует OpenSSL.
В противном случае, он не будет собран или установлен.
При компиляции с использованием OpenSSL версии 3.0.0 и выше, для использования устаревших шифров, таких как DES или Blowfish, необходимо активировать устаревший провайдер в файле конфигурации openssl.cnf
.
F.34.6.2. Обработка NULL значений #
Как и в стандартном SQL, все функции возвращают NULL, если хотя бы один из аргументов является NULL. Это может создавать уязвимости безопасности при небрежном использовании.
F.34.6.3. Ограничения безопасности #
Все функции pgcrypto
выполняются внутри сервера базы данных.
Это означает, что все данные и пароли передаются между pgcrypto
и клиентскими
приложениями в открытом виде. Поэтому необходимо:
Подключайтесь локально или используйте SSL-соединения.
Доверяйте и системному администратору, и администратору базы данных.
Если вы не можете, то лучше выполнять криптографию внутри клиентского приложения.
Реализация не устойчива к атакам через боковые каналы. Например, время, необходимое для завершения функции расшифровки pgcrypto
, может варьироваться в зависимости от размера шифротекста.
F.34.7. Автор #
Марко Креен <markokr@gmail.com>
pgcrypto
использует код из следующих источников:
Алгоритм | Автор | Источник происхождения |
---|---|---|
DES crypt | David Burren и другие | FreeBSD libcrypt |
MD5 криптография | Poul-Henning Kamp | FreeBSD libcrypt |
Blowfish crypt | Solar Designer | www.openwall.com |