Статьи

Выбор криптографических алгоритмов Java, часть 1 — хеширование

Аннотация

Это первая из трех статей блога, посвященных криптографическим алгоритмам Java. В серии рассказывается, как реализовать следующее:

  1. Хеширование с помощью SHA – 512
  2. Симметричное шифрование с одним ключом с помощью AES – 256
  3. Асимметричное шифрование с открытым / закрытым ключом с помощью RSA – 4096

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

отказ

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

Требования

Я сделал всю работу для этого поста, используя следующие основные технологии. Вы можете сделать то же самое с разными технологиями или версиями, но без гарантий.

  • Java 1.8.0_152_x64
  • NetBeans 8.2 (сборка 201609300101)
  • Maven 3.0.5 (в комплекте с NetBeans)

Скачать

Посетите мою страницу GitHub, чтобы увидеть все мои проекты с открытым исходным кодом. Код для этого поста находится в проекте: thoth-cryptography

Хэш

Около

Хеширование — это односторонний криптографический алгоритм, который принимает сообщение любой длины и выдает повторяемый, фиксированный по длине и односторонний дайджест (хэш) сообщения. Будучи односторонним, предполагается, что невозможно восстановить исходное сообщение из хэша. Одинаковые сообщения всегда будут генерировать один и тот же хэш.

Хеш может использоваться для аутентификации исходного сообщения. Обычное использование хеширования — проверка паролей. Вместо хранения самого пароля сохраняется хеш пароля. Для проверки пароля сохраненный хеш сравнивается с новым хешем входящего пароля во время процесса входа в систему.

Поскольку идентичные сообщения генерируют один и тот же хэш, для повышения его безопасности используется солт-значение (Salt, 2017, пункт 1). Рассмотрим случай, когда один и тот же пароль используется несколькими пользователями. Солт-значение в сочетании с исходным паролем допускает уникальные значения хеш-функции. Это важно, потому что если хэшированные значения когда-либо скомпрометированы, идентичные хеши позволяют хакеру узнать, что эти пароли одинаковы.

SHA-512

Исследования, проведенные на сегодняшний день, показывают, что лучшим и наиболее безопасным алгоритмом хеширования является SHA-512, в котором используются 64-битные слова (Secure Hash Algorithms, 2017, пункт 2). Давайте посмотрим на пример.

ПРИМЕЧАНИЕ. Не используйте MD5 в качестве безопасного хэша. У него много уязвимостей (MD5, 2017, пункт 1). Ограничьте использование MD5 контрольными суммами и проверкой данных.

пример

В листинге 1 приведен модульный тест ShaTest.java, демонстрирующий, как хэшировать. В листинге 2 приведен класс Sha.java, который выполняет хэширование.

Листинг 1 — класс ShaTest.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package org.thoth.security.hash;
 
import java.util.Optional;
import org.junit.Assert;
import org.junit.Test;
 
/**
 * @author Michael Remijan [email protected] @mjremijan
 */
public class ShaTest {
 
    @Test
    public void test_hash_with_optional_to_hex() throws Exception {
        // setup
        String username = "mjremijan";
        String password = "super!secret";
        Sha sha = new Sha();
 
        // test
        String asHex
            = sha.hashToHex(password, Optional.of(username));
 
        // assert
        Assert.assertEquals(
              "F38CD5290D11B20159E36740843A8D93CFDFA395CF594F328613EF5C7BA42D9EAC00BF3EE47B7E8CE1587040B36365F05C8E15E9392C288A1D7C4CFB66097848"
            , asHex);
    }
 
    @Test
    public void test_hash_without_optional_to_hex() throws Exception {
        // setup
        String password = "super!secret";
        Sha sha = new Sha();
 
        // test
        String asHex
            = sha.hashToHex(password, Optional.empty());
 
        // assert
        Assert.assertEquals(
              "516A1FE9D87FE5B953D91B48B1A2FFA5AE5F670914C1B6FE0835D8877918DC4E8BC8FB8CCD520DBA940C21B4F294DFD1B4EFF2E06AB110C6A06E35068251C1DD"
            , asHex);
    }
 
 
    @Test
    public void test_hash_with_optional_to_base64() throws Exception {
        // setup
        String username = "mjremijan";
        String password = "super!secret";
        Sha sha = new Sha();
 
        // test
        String asBase64
            = sha.hashToBase64(password, Optional.of(username));
 
        // assert
        Assert.assertEquals(
              "84ZVKQ0RSGFZ42DAHDQNK8/FO5XPWU8YHHPVXHUKLZ6SAL8+5HT+JOFYCECZY2XWXI4V6TKSKIODFEZ7ZGL4SA=="
            , asBase64);
    }
 
 
    @Test
    public void test_hash_without_optional_to_base64() throws Exception {
        // setup
        String password = "super!secret";
        Sha sha = new Sha();
 
        // test
        String asBase64
            = sha.hashToBase64(password, Optional.empty());
 
        // assert
        Assert.assertEquals(
              "UWOF6DH/5BLT2RTISAL/PA5FZWKUWBB+CDXYH3KY3E6LYPUMZVINUPQMIBTYLN/RTO/Y4GQXEMAGBJUGGLHB3Q=="
            , asBase64);
    }
}

Листинг 2 — Класс Sha.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.thoth.security.hash;
 
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Optional;
 
/**
 * @author Michael Remijan [email protected] @mjremijan
 */
public class Sha {
 
    public String hashToHex(String hashMe, Optional<String> salt)
    throws NoSuchAlgorithmException, UnsupportedEncodingException {
        byte[] bytes
            = hash(hashMe, salt);
 
        StringBuilder sp
            = new StringBuilder();
 
        for (int i = 0; i < bytes.length; i++) {
            sp.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
 
        return sp.toString().toUpperCase();
    }
 
    public String hashToBase64(String hashMe, Optional<String> salt)
    throws NoSuchAlgorithmException, UnsupportedEncodingException {
        return Base64.getEncoder().encodeToString(
            hash(hashMe, salt)
        ).toUpperCase();
    }
 
    public byte[] hash(String hashMe, Optional<String> salt)
    throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md
            = MessageDigest.getInstance("SHA-512");
 
        md.update(hashMe.getBytes("UTF-8"));
        salt.ifPresent(s -> {
            try { md.update(s.getBytes("UTF-8")); } catch (Exception e) {throw new RuntimeException(e);}
        });
 
        return md.digest();
    }
}

Резюме

Хеширование довольно легко. Выберите надежный алгоритм хеширования, такой как SHA-512, для защиты данных вашего приложения. Избегайте MD5 для защиты данных. Будьте в курсе, какие алгоритмы сильны и безопасны. Обновите приложение, если вы используете более старый алгоритм, который имеет уязвимости или скомпрометирован.

Рекомендации

Соль (криптография). (2017, 3 ноября). Wikipedia. Получено с https://en.wikipedia.org/wiki/Salt_(cryptography) .

Безопасные алгоритмы хеширования. (2017, 25 ноября). Wikipedia. Получено с https://en.wikipedia.org/wiki/Secure_Hash_Algorithms .

MD5. (2017, 22 ноября). Wikipedia. Получено с https://en.wikipedia.org/wiki/MD5 .

Опубликовано на Java Code Geeks с разрешения Майкла Ремиджана, партнера нашей программы JCG. См. Оригинальную статью здесь: Выбор алгоритмов шифрования Java. Часть 1. Хеширование.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.