Теоретически невозможно для выходов, которые короче, чем вход.Это тривиально следует из принципа дырки в пиджоне.
Вы можете использовать асимметричное шифрование, когда вы выбрасываете закрытый ключ.Таким образом, это технически шифрование без потерь, но никто не сможет легко перевернуть его.Обратите внимание, что это на намного медленнее, чем обычное хеширование, и результат будет больше, чем на входе.
Но вероятность столкновения падает экспоненциально с размером хэша.Хороший 256-битный хэш не содержит столкновений для всех практических целей.И под этим я подразумеваю, что хеширование в течение миллиардов лет со всеми компьютерами в мире почти наверняка не вызовет коллизий.
Ваш расширенный вопрос показывает две проблемы.
Какая пользабыло бы что-то подобное?Хорошо, представьте браузер со списком веб-сайтов, которые следует исключить из истории (например, сайты NSFW).Если этот список сохраняется в незашифрованном или зашифрованном виде с помощью ключа, известного в системе, он может быть прочитан не только браузером, но и боссами, женами и т. Д.
Если вместо этого адреса веб-сайтов хранятся хэшированными, они могут 'не может быть прочитано, но браузер может проверить, присутствует ли сайт в списке.
В этом случае использование грубой силы тривиально.Просто найдите список всех доменов / файл зоны.Не удивлюсь, если хороший список можно скачать где-нибудь.
Использование обычной хэш-функции может привести к ложным срабатываниям (хотя и маловероятно).
Вероятность столкновенияхэш намного ниже (особенно если у вас нет злоумышленника, который пытается спровоцировать коллизию в этом сценарии), чем вероятность аппаратной ошибки.
Так что мой вывод - объединить секрет с медленным хешем.
byte[] secret=DeriveKeyFromPassword(pwd, salt, enough iterations for this to take perhaps a second)
и затем для фактического хэша используйте KDF, снова комбинируя секрет и имя домена.