Cipher.getInstance: примеры (JAVA)
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 с явным параметром
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)
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. Потоковая обработка больших данных
// Пример с 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 и провайдерами аппаратного уровня
// Пример получения Cipher для ключа из AndroidKeyStore
Cipher c = Cipher.getInstance("AES/GCM/NoPadding", "AndroidKeyStore");
// ключ извлекается через KeyStore.getInstance("AndroidKeyStore") и init с ним
c.getProvider(): AndroidKeyStore (аппаратная реализация, если доступна)
6. Явная проверка доступных трансформаций у провайдеров
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, обёртка ключей, потоковая обработка и использование провайдеров аппаратной криптографии.