Руководство по классу шифров

Руководство по классу шифров

1. обзор

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

Сообщение, обозначенное какplaintext, зашифровано с использованием алгоритма шифрования - acipher - генерирующегоciphertext, который может быть прочитан только авторизованными пользователями с помощью дешифрования.

В этой статье мы подробно описываемthe core Cipher class, which provides cryptographic encryption and decryption functionality на Java.

2. Класс шифра

Расширение Java Cryptography Extension (JCE) - этоpart of the Java Cryptography Architecture (JCA), который предоставляет приложению криптографические шифры для шифрования и дешифрования данных, а также хеширования частных данных.

КлассCipher, расположенный в пакетеjavax.crypto, образует ядро ​​инфраструктуры JCE, обеспечивая функциональные возможности для шифрования и дешифрования.

2.1. Создание шифров

Чтобы создать экземпляр объектаCipher, мыcall the static getInstance method, passing the name of the requested transformation. При желании может быть указано имя провайдера.

Напишем пример класса, иллюстрирующий созданиеCipher:

public class Encryptor {

    public byte[] encryptMessage(byte[] message, byte[] keyBytes)
      throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        //...
    }
}

ПреобразованиеAES/ECB/PKCS5Padding сообщает методуgetInstance создать экземпляр объектаCipher как шифрAES с ECBmode of operation и PKCS5padding scheme.

Мы также можем создать экземпляр объектаCipher, указав только алгоритм преобразования:

Cipher cipher = Cipher.getInstance("AES");

В этом случае используются специфичные для провайдера значения по умолчанию для режима и схемы заполнения.

NoSuchAlgorithmException выдается, если преобразование являетсяnull, пустым, в недопустимом формате или указанная реализация алгоритма шифрования недоступна от указанного поставщика.

NoSuchPaddingException выдается, если преобразование содержит схему заполнения, которая недоступна.

2.2. Keys

ИнтерфейсKey представляет ключи для криптографических операций. Ключи - это непрозрачные контейнеры, которые содержат закодированный ключ, формат кодирования ключа и его криптографический алгоритм.

Ключи обычно получают черезkey generators, сертификаты илиkey specifications с помощьюkey factory.

Давайте создадим симметричныйKey из предоставленных байтов ключа:

SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");

2.3. Инициализация шифра

We call the init() method to initialize the Cipher object сKey илиCertificate иopmode, указывающим режим работы шифра.

Необязательноwe can pass in a source of randomness. По умолчанию используется реализацияSecureRandom установленного поставщика с наивысшим приоритетом. Если ничего не найдено, будет использован системный источник.

We can specify a set of algorithm-specific parameters optionally. Например, мы можем передатьIvParameterSpec вspecify an initialization vector.

Вот доступные режимы работы шифра:

  • ENCRYPT_MODE: инициализировать объектcipher в режиме шифрования

  • DECRYPT_MODE: инициализировать объектcipher в режим дешифрования

  • WRAP_MODE: инициализировать объектcipher в режимеkey-wrapping

  • UNWRAP_MODE: инициализировать объектcipher в режимеkey-unwrapping

Давайте инициализируем объектCipher:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// ...

InvalidKeyException выдается, если предоставленный ключ не подходит для инициализации шифра, например, когда длина / кодировка ключа недействительны.

Он также выдается, когда шифру требуются определенные параметры алгоритма, которые нельзя определить по ключу, или если размер ключа превышает максимально допустимый размер ключа (определяется из файлов политикиconfigured JCE jurisdiction).

Давайте посмотрим на пример с использованиемCertificate:

public byte[] encryptMessage(byte[] message, Certificate certificate)
  throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {

    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, certificate);
    // ...
}

ОбъектCipher получает открытый ключ для шифрования данных из сертификата, вызывая методgetPublicKey.

2.4. Шифрование и дешифрование

После инициализации объектаCipher мы вызываем методdoFinal() для выполнения операции шифрования или дешифрования. Этот метод возвращает байтовый массив, содержащий зашифрованное или расшифрованное сообщение.

МетодdoFinal() также сбрасывает объектCipher в состояние, в котором он был ранее инициализирован с помощью вызова методаinit(), делая объектCipher доступным для шифрования или дешифрования дополнительных Сообщения.

Давайте вызовемdoFinal в нашем методеencryptMessage:

public byte[] encryptMessage(byte[] message, byte[] keyBytes)
  throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException,
    BadPaddingException, IllegalBlockSizeException {

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    return cipher.doFinal(message);
}

Чтобы выполнить операцию дешифрования, мы меняемopmode наDECRYPT_MODE:

public byte[] decryptMessage(byte[] encryptedMessage, byte[] keyBytes)
  throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);
    return cipher.doFinal(encryptedMessage);
}

2.5. Провайдеры

Разработан для использованияprovider-based architecture,JCE allows for qualified cryptography libraries such as BouncyCastle to be plugged in as security providers and new algorithms to be added seamlessly.

Теперь давайте добавим BouncyCastle в качестве поставщика безопасности. We can add a security provider either statically or dynamically.с

To add BouncyCastle statically, we modify the java.security file находится в папке<JAVA_HOME>/jre/lib/security.

Добавляем строку в конец списка:

...
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider

При добавлении свойства провайдера ключ свойства имеет форматsecurity.provider.N, где числоN на единицу больше, чем последнее в списке.

We can also add the BouncyCastle security provider dynamically без изменения файла безопасности:

Security.addProvider(new BouncyCastleProvider());

Теперь мы можем указать провайдера во время инициализации шифра:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");

BC указывает BouncyCastle в качестве поставщика. Мы можем получить список зарегистрированных провайдеров с помощью методаSecurity.getProviders().

3. Тестирование шифрования и дешифрования

Давайте напишем пример теста, чтобы проиллюстрировать шифрование и дешифрование сообщений.

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

@Test
public void whenIsEncryptedAndDecrypted_thenDecryptedEqualsOriginal()
  throws Exception {

    String encryptionKeyString =  "thisisa128bitkey";
    String originalMessage = "This is a secret message";
    byte[] encryptionKeyBytes = encryptionKeyString.getBytes();

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKey secretKey = new SecretKeySpec(encryptionKeyBytes, "AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    byte[] encryptedMessageBytes = cipher.doFinal(message.getBytes());

    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    byte[] decryptedMessageBytes = cipher.doFinal(encryptedMessageBytes);
    assertThat(originalMessage).isEqualTo(new String(decryptedMessageBytes));
}

4. Заключение

В этой статье мы обсудили классCipher и представили примеры использования. Более подробную информацию о классеCipher и JCE Framework можно найти вclass documentation иJava Cryptography Architecture (JCA) Reference Guide.

Реализация всех этих примеров и фрагментов кодаcan be foundover on Github; это проект на основе Maven, поэтому его будет легко импортировать и запускать как есть.