Cipher.getInstance: примеры (JAVA)

Использование Cipher.getInstance
Раздел: Шифрование и хеширование (Cryptography, MessageDigest)
Cipher.getInstance(String transformation): Cipher

Описание метода Cipher.getInstance

Метод Cipher.getInstance принадлежит пакету javax.crypto и служит для получения экземпляра класса javax.crypto.Cipher, представляющего криптографический преобразователь данных. Возвращаемый объект выполняет операции шифрования, расшифровки, обёртывания ключей и распаковки в зависимости от инициализации.

Сигнатуры метода:

  • public static Cipher getInstance(String transformation)
  • public static Cipher getInstance(String transformation, String provider)
  • public static Cipher getInstance(String transformation, java.security.Provider provider)

Параметр transformation представляет собой строку в формате "алгоритм[/режим[/паддинг]]". Примеры: "AES/CBC/PKCS5Padding", "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "AES/GCM/NoPadding". Если указан только алгоритм (например, "AES"), то будет использован режим и паддинг по умолчанию у провайдера.

Параметры провайдера (второй и третий варианты) позволяют указать конкретную реализацию криптографических примитивов: строковое имя провайдера или объект Provider. Если провайдер не указан, поиск реализации выполняется в порядке, определённом списком установленных провайдеров (Security.getProviders()).

Возвращаемое значение: экземпляр Cipher, готовый к инициализации через init(int opmode, Key key, AlgorithmParameterSpec params) или другие перегруженные варианты. До вызова init экземпляр находится в неинициализированном состоянии и не может выполнять операции над данными.

Типичные исключения:

  • NoSuchAlgorithmException - если указанный алгоритм/режим/паддинг не поддерживается текущими провайдерами.
  • NoSuchPaddingException - если указанный паддинг не найден.
  • NoSuchProviderException - при попытке получить провайдера по имени, которого нет.
  • NullPointerException - при передаче null в качестве обязательных аргументов.

Особенности использования:

  • Формат трансформации чувствителен к синтаксису. Полная спецификация: алгоритм, опционально режим, опционально паддинг.
  • Некоторые алгоритмы требуют дополнительных параметров при инициализации (например, IV для CBC, GCMParameterSpec для GCM, OAEPParameterSpec для OAEP).
  • Состояние объекта Cipher - одно из множества: необходимо вызывать init перед шифрованием/расшифровкой; повторная инициализация допускается для смены режима.
  • Поддержка длин ключей может зависеть от политик JDK (ранее требовались JCE Unlimited Strength Jurisdiction Policy Files - с определённых версий ограничение снято по умолчанию).

Короткие примеры использования

Ниже приведены компактные примеры с отображением кода и демонстрацией результатов. Примеры используют фиксированные ключи и IV для воспроизводимости примеров вывода.

1. AES/CBC/PKCS5Padding - шифрование и расшифровка

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

byte[] key = "0123456789abcdef".getBytes(); // 16 байт
byte[] iv = "abcdef9876543210".getBytes();  // 16 байт
SecretKeySpec sk = new SecretKeySpec(key, "AES");
IvParameterSpec ivp = new IvParameterSpec(iv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, sk, ivp);
byte[] ct = c.doFinal("Hello, мир".getBytes("UTF-8"));
System.out.println("Encrypted(Base64): " + Base64.getEncoder().encodeToString(ct));

c.init(Cipher.DECRYPT_MODE, sk, ivp);
byte[] pt = c.doFinal(ct);
System.out.println("Decrypted: " + new String(pt, "UTF-8"));
Encrypted(Base64): q1s8h3v2yX0mB9k2P3z4sA==
Decrypted: Hello, мир

2. AES/GCM/NoPadding - шифрование с аутентификацией (GCM)

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

byte[] key = new byte[16]; // нулевой ключ для примера
byte[] iv = new byte[12];  // 12 байт для GCM
SecretKeySpec sk = new SecretKeySpec(key, "AES");
GCMParameterSpec gcm = new GCMParameterSpec(128, iv);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, sk, gcm);
byte[] ct = c.doFinal("data".getBytes());
System.out.println(Base64.getEncoder().encodeToString(ct));
q83f5l2v... (пример двоичных данных в Base64)

3. RSA/ECB/OAEPWithSHA-256AndMGF1Padding - асимметричное шифрование

import javax.crypto.Cipher;
import java.security.*;

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Cipher c = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");

c.init(Cipher.ENCRYPT_MODE, kp.getPublic());
byte[] ct = c.doFinal("test".getBytes());
System.out.println(java.util.Base64.getEncoder().encodeToString(ct));

c.init(Cipher.DECRYPT_MODE, kp.getPrivate());
byte[] pt = c.doFinal(ct);
System.out.println(new String(pt));
Base64-шифротекст: AbCdEf... (длинная строка)
Расшифровка: test

4. Указание провайдера

Cipher c1 = Cipher.getInstance("AES/CBC/PKCS5Padding");
// если установлен конкретный провайдер
Cipher c2 = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
c1.getProvider(): SunJCE (или другой по умолчанию)
c2.getProvider(): SunJCE (если провайдер доступен)

Похожие API в Java

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

  • Signature.getInstance - для цифровых подписей; предпочтителен при необходимости проверки целостности и аутентичности с помощью алгоритмов подписи.
  • Mac.getInstance - для вычисления MAC (HMAC); используется, когда требуется симметричная проверка целостности без шифрования.
  • KeyAgreement.getInstance - для согласования ключей (например, DH, ECDH); применяется при необходимости генерации общего секрета между участниками.
  • CipherInputStream / CipherOutputStream - обёртки для потоковой обработки данных с использованием Cipher; удобны при работе с потоками и большими объёмами данных.

Выбор между ними определяется задачей: шифрование/расшифровка - Cipher, подпись/проверка - Signature, вычисление HMAC - Mac, обмен ключами - KeyAgreement.

Аналоги в других языках

Ниже кратко перечислены эквиваленты метода получения шифраторов и основные отличия от Java:

  • PHP: функции openssl_encrypt/openssl_decrypt, инициализация через openssl_cipher_iv_length. Отличие - удобные однофункциональные вызовы вместо отдельного объекта Cipher.
    $key = hex2bin('00112233445566778899aabbccddeeff');
    $iv = hex2bin('0102030405060708');
    $ct = openssl_encrypt('hello', 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    echo base64_encode($ct);
    QmFzZTY0... (пример)
  • JavaScript (Web Crypto): crypto.subtle.encrypt/crypto.subtle.decrypt. Асинхронный промис-API, строгая модель ключей и типов, работает в браузере и в ограниченном виде в Node.js.
    const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['encrypt']);
    const ct = await crypto.subtle.encrypt({name: 'AES-GCM', iv}, key, data);
    ArrayBuffer с шифротекстом
  • Node.js: crypto.createCipheriv/createDecipheriv. Императивный потоковый API и одна функция для шифрования.
    const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    let ct = Buffer.concat([cipher.update('hi', 'utf8'), cipher.final()]);
    console.log(ct.toString('base64'));
    QmFzZTY0... (пример)
  • Python: библиотека cryptography или PyCryptodome. В отличие от Java, чаще используются объекты-обёртки с явной конструкцией шифра и режима.
    from Cryptodome.Cipher import AES
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ct = cipher.encrypt(pad(b'hello', 16))
    b'\\x9f\\x8a...' (байты)
  • C# (.NET): Aes.Create(), CreateEncryptor. API объектно-ориентирован и близок по смыслу к Java Cipher, но типы и сигнатуры отличаются.
    using (var aes = Aes.Create()) {
      aes.Key = key;
      aes.IV = iv;
      var encryptor = aes.CreateEncryptor();
    }
    
    byte[] с шифротекстом
  • Go: пакет crypto/cipher (NewGCM, NewCBCEncrypter). В Go режимы представлены отдельными конструкторами, padding часто выполняется вручную.
    block, _ := aes.NewCipher(key)
    gcm, _ := cipher.NewGCM(block)
    ct := gcm.Seal(nil, iv, plaintext, nil)
    []byte с шифротекстом
  • Kotlin: использует ту же JCA как Java, синтаксически более компактный код.
  • Lua: через библиотеки-обёртки (например, lua-openssl) инициализация и вызов похожи на OpenSSL, но экосистема разбросана.
  • SQL (PostgreSQL): расширение pgcrypto предоставляет функции encrypt/decrypt и pgp_sym_encrypt, интерфейс процедурный и неинтегрированный с JCA.

Ключевое отличие Java Cipher - единая модель JCA/JCE с провайдерами, позволяющая подменять реализации и управлять безопасностью через политики провайдеров. Другие языки часто предоставляют высокоуровневые функции или используют нативные библиотеки.

Типичные ошибки и их признаки

Частые проблемы при работе с Cipher.getInstance и типичные сообщения ошибок:

  • NoSuchAlgorithmException - возникает, если трансформация не распознана провайдерами.
    Cipher.getInstance("AES/FOO/Bar");
    java.security.NoSuchAlgorithmException: AES/FOO/Bar SecretKeyFactory not available
  • NoSuchPaddingException - указывает на неизвестный паддинг.
    Cipher.getInstance("AES/CBC/UNKNOWN");
    javax.crypto.NoSuchPaddingException: Padding scheme not available
  • NoSuchProviderException - при указании несуществующего провайдера.
    Cipher.getInstance("AES/CBC/PKCS5Padding", "NoSuchProvider");
    java.security.NoSuchProviderException: NoSuchProvider
  • InvalidKeyException или InvalidAlgorithmParameterException - при неверном ключе или параметрах (например, длина IV не равна требуемой).
    SecretKeySpec sk = new SecretKeySpec(new byte[8], "AES");
    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
    c.init(Cipher.ENCRYPT_MODE, sk, new IvParameterSpec(new byte[16]));
    java.security.InvalidKeyException: Invalid AES key length: 8 bytes
  • BadPaddingException и IllegalBlockSizeException - при расшифровке с неверным ключом или повреждёнными данными.
    // расшифровка с неверным ключом
    byte[] badCt = ...;
    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
    c.init(Cipher.DECRYPT_MODE, wrongKey, ivSpec);
    c.doFinal(badCt);
    javax.crypto.BadPaddingException: Given final block not properly padded

Рекомендация по диагностике: проверять доступность провайдера и поддерживаемые трансформации через Security.getProviders() и чтение документации конкретного провайдера.

Изменения и примечания по версиям JDK

Основные изменения, затрагивающие поведение при использовании Cipher.getInstance в недавних релизах:

  • Поддержка блоковых режимов и современных алгоритмов (например, AES/GCM) появилась и была расширена в Java 7/8. Во многих сборках GCM доступен из коробки.
  • Ограничения на длину ключей: начиная с Java 8u162 и в Java 9+, политика неограниченной длины ключа включена по умолчанию, ранее требовалась установка JCE Unlimited Strength Jurisdiction Policy Files.
  • Дополнения в списке доступных OAEP-подписей (например, OAEPWithSHA-256AndMGF1Padding) появились по мере обновления провайдера SunJCE и сторонних провайдеров. Непосредственные изменения зависят от поставщика реализации.
  • Метод getInstance сам по себе не подвергался депрекации; дальнейшие улучшения связаны с провайдерами и обновлениями криптографических примитивов.

Расширенные и редкие варианты применения

Дополнительные сценарии и нестандартные приёмы при работе с Cipher.getInstance с пояснениями и примерами вывода.

1. Указание OAEP с явным параметром

Пример java
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.MGF1ParameterSpec;
import java.security.spec.PSource;
import java.security.*;

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
OAEPParameterSpec oaep = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
Cipher c = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
c.init(Cipher.ENCRYPT_MODE, kp.getPublic(), oaep);
byte[] ct = c.doFinal("secret".getBytes());
System.out.println(java.util.Base64.getEncoder().encodeToString(ct));
Base64-шифротекст: XYz... (пример)

2. Использование Cipher для упаковки и распаковки ключей (wrap/unwrap)

Пример java
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey dataKey = kg.generateKey();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");

// обёртка симметричного ключа
c.init(Cipher.WRAP_MODE, kp.getPublic());
byte[] wrapped = c.wrap(dataKey);
System.out.println(wrapped.length);
// распаковка
c.init(Cipher.UNWRAP_MODE, kp.getPrivate());
Key unwrapped = c.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
System.out.println(unwrapped.getAlgorithm());
128
AES

3. Потоковая обработка больших данных

Пример java
// Пример с CipherOutputStream для записи зашифрованного файла
import javax.crypto.CipherOutputStream;
import java.io.FileOutputStream;

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
// init omitted: предполагается наличие sk и ivp
try (FileOutputStream fos = new FileOutputStream("out.enc");
     CipherOutputStream cos = new CipherOutputStream(fos, c)) {
    cos.write(largePlainBytes);
}
System.out.println("Запись в out.enc завершена");
Запись в out.enc завершена

4. Повторное использование и многопоточные нюансы

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

5. Работа с AndroidKeyStore и провайдерами аппаратного уровня

Пример java
// Пример получения Cipher для ключа из AndroidKeyStore
Cipher c = Cipher.getInstance("AES/GCM/NoPadding", "AndroidKeyStore");
// ключ извлекается через KeyStore.getInstance("AndroidKeyStore") и init с ним
c.getProvider(): AndroidKeyStore (аппаратная реализация, если доступна)

6. Явная проверка доступных трансформаций у провайдеров

Пример java
for (java.security.Provider p : java.security.Security.getProviders()) {
  for (java.util.Map.Entry<Object,Object> e : p.entrySet()) {
    String k = (String)e.getKey();
    if (k.startsWith("Cipher.")) System.out.println(p.getName() + ": " + k.substring(7));
  }
}
SunJCE: AES/CBC/PKCS5Padding
SunJCE: AES/GCM/NoPadding
BC: RSA/ECB/OAEPWithSHA-256AndMGF1Padding
... (пример списка)

Примеры показывают наиболее распространённые и продвинутые сценарии: явная спецификация параметров OAEP, обёртка ключей, потоковая обработка и использование провайдеров аппаратной криптографии.

джава Cipher.getInstance function comments

En
Cipher.getInstance Returns a Cipher object that implements the specified transformation