Bouncy Castle для Java: Полное руководство по криптографии и шифрованию
1. Что это и зачем нужна
Bouncy Castle (часто называемая просто BC) — это одна из самых мощных и широко используемых криптографических библиотек для платформы Java (и .NET). Она предоставляет разработчикам обширный набор API для реализации шифрования, цифровых подписей, хеширования, генерации ключей и работы с сертификатами. В отличие от стандартного Java Cryptography Extension (JCE), Bouncy Castle предлагает поддержку гораздо большего количества алгоритмов, включая многие устаревшие, экспериментальные и редко используемые, что делает её незаменимым инструментом для специалистов по безопасности и разработчиков, работающих с нестандартными протоколами.
Зачем она нужна? Во-первых, стандартная библиотека Java (JCA/JCE) не включает в себя многие современные и специализированные алгоритмы (например, Twofish, Serpent, GOST, SM2/SM3/SM4). Bouncy Castle восполняет этот пробел. Во-вторых, она предоставляет "легковесную" (Lightweight API) реализацию, которая не требует установки провайдера безопасности (Provider) в глобальную JVM, что удобно для встраиваемых систем и приложений, где важна изоляция. В-третьих, библиотека активно поддерживается, проходит аудит безопасности и является де-факто стандартом для многих opensource проектов (например, OpenSSL для Java).
Bouncy Castle состоит из двух основных компонентов: провайдер (bcprov-jdk18on), который интегрируется с архитектурой безопасности Java, и PKIX/CMS/SMIME/TLS пакеты (bcpkix-jdk18on, bcmail-jdk18on, bctls-jdk18on), которые реализуют высокоуровневые протоколы, такие как S/MIME, TLS и работу с сертификатами X.509. Эта статья будет фокусироваться на ядре библиотеки — провайдере и его легковесном API.
2. Установка
Bouncy Castle распространяется через Maven Central. Для использования в проекте на Java (Maven, Gradle, SBT) добавьте следующие зависимости в ваш файл сборки.
Для Maven (pom.xml):
<!-- Основной провайдер (обязательно) -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version> <!-- Проверьте актуальную версию на mvnrepository.com -->
</dependency>
<!-- Для работы с сертификатами, CMS, S/MIME (опционально) -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
Для Gradle (build.gradle):
implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.78.1' // Опционально
Важно: Если вы используете старые версии Java (до 8), вам потребуется версия bcprov-jdk15on. Для Java 8+ используйте bcprov-jdk18on. После добавления зависимости, в коде необходимо зарегистрировать провайдер (если вы используете стандартный JCE API) или работать напрямую с легковесным API.
3. Быстрый старт — минимальный рабочий пример
Простейший пример: шифрование строки с использованием алгоритма AES-256 в режиме GCM (аутентифицированное шифрование) через легковесный API.
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.util.encoders.Hex;
import java.security.SecureRandom;
public class QuickStart {
public static void main(String[] args) throws Exception {
// 1. Генерируем ключ и IV (Initialization Vector)
SecureRandom secureRandom = new SecureRandom();
byte[] keyBytes = new byte[32]; // 256 бит
byte[] ivBytes = new byte[12]; // 96 бит для GCM
secureRandom.nextBytes(keyBytes);
secureRandom.nextBytes(ivBytes);
KeyParameter key = new KeyParameter(keyBytes);
ParametersWithIV params = new ParametersWithIV(key, ivBytes);
// 2. Создаем шифр AES-GCM
GCMBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(true, params); // true = режим шифрования
// 3. Шифруем данные
byte[] plaintext = "Hello, Bouncy Castle!".getBytes();
byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
int len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
len += cipher.doFinal(ciphertext, len); // Завершаем шифрование
// 4. Выводим результат в hex
System.out.println("Зашифровано: " + Hex.toHexString(ciphertext));
// 5. Дешифрование
cipher.init(false, params); // false = режим дешифрования
byte[] decrypted = new byte[cipher.getOutputSize(ciphertext.length)];
int decLen = cipher.processBytes(ciphertext, 0, ciphertext.length, decrypted, 0);
decLen += cipher.doFinal(decrypted, decLen);
System.out.println("Расшифровано: " + new String(decrypted, 0, decLen));
}
}
4. Основные методы/функции/классы
Рассмотрим ключевые классы и методы Bouncy Castle. Все примеры используют легковесный API (пакет org.bouncycastle.crypto), так как он не зависит от глобального провайдера.
4.1. org.bouncycastle.crypto.engines.AESEngine
Сигнатура: public class AESEngine implements BlockCipher
Что делает: Реализует базовый алгоритм AES (Rijndael). Используется как строительный блок для режимов шифрования (CBC, GCM, CTR и т.д.).
Пример:
BlockCipher engine = new AESEngine();
KeyParameter key = new KeyParameter(keyBytes);
engine.init(true, key); // true - шифрование
byte[] input = new byte[16]; // AES работает с блоками по 16 байт
byte[] output = new byte[16];
engine.processBlock(input, 0, output, 0); // Шифрует один блок
4.2. org.bouncycastle.crypto.modes.GCMBlockCipher
Сигнатура: public class GCMBlockCipher extends BufferedBlockCipher
Что делает: Реализует аутентифицированный режим шифрования Galois/Counter Mode (GCM). Обеспечивает конфиденциальность и целостность данных (аутентификацию).
Пример:
GCMBlockCipher gcm = new GCMBlockCipher(new AESEngine());
gcm.init(true, new AEADParameters(key, 128, ivBytes)); // 128 - размер MAC-тега в битах
byte[] ciphertext = new byte[gcm.getOutputSize(plaintext.length)];
int len = gcm.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
gcm.doFinal(ciphertext, len); // Добавляет MAC-тег в конец
4.3. org.bouncycastle.crypto.generators.SCrypt
Сигнатура: public static byte[] generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
Что делает: Реализует функцию деривации ключа scrypt, устойчивую к аппаратным атакам (ASIC/FPGA). Используется для хеширования паролей.
Пример:
byte[] password = "my_secret_password".getBytes();
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
byte[] derivedKey = SCrypt.generate(password, salt, 16384, 8, 1, 32);
System.out.println("Scrypt hash: " + Hex.toHexString(derivedKey));
4.4. org.bouncycastle.crypto.generators.Argon2BytesGenerator
Сигнатура: public void init(Argon2Parameters params); public int generateBytes(byte[] output, int outputOffset, int outputLen)
Что делает: Реализует современную функцию деривации ключа Argon2 (победитель конкурса PHC). Рекомендуется для хеширования паролей.
Пример:
Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
.withSalt(salt)
.withParallelism(1)
.withMemoryAsKB(65536) // 64 MB
.withIterations(3);
Argon2BytesGenerator gen = new Argon2BytesGenerator();
gen.init(builder.build());
byte[] result = new byte[32];
gen.generateBytes(password, result);
System.out.println("Argon2 hash: " + Hex.toHexString(result));
4.5. org.bouncycastle.crypto.digests.SHA256Digest
Сигнатура: public class SHA256Digest implements ExtendedDigest
Что делает: Вычисляет хеш SHA-256. Классы SHA384Digest, SHA512Digest, SM3Digest работают аналогично.
Пример:
SHA256Digest digest = new SHA256Digest();
byte[] message = "Data to hash".getBytes();
digest.update(message, 0, message.length);
byte[] hash = new byte[digest.getDigestSize()];
digest.doFinal(hash, 0);
System.out.println("SHA-256: " + Hex.toHexString(hash));
4.6. org.bouncycastle.crypto.signers.ECDSASigner
Сигнатура: public class ECDSASigner implements DSA
Что делает: Реализует алгоритм цифровой подписи ECDSA (Elliptic Curve Digital Signature Algorithm).
Пример:
// Генерация ключей EC
ECKeyPairGenerator gen = new ECKeyPairGenerator();
SecureRandom random = new SecureRandom();
gen.init(new ECKeyGenerationParameters(secp256r1, random));
AsymmetricCipherKeyPair pair = gen.generateKeyPair();
// Подпись
ECDSASigner signer = new ECDSASigner();
signer.init(true, new ParametersWithRandom(pair.getPrivate(), random));
BigInteger[] signature = signer.generateSignature("Message".getBytes());
// Верификация
signer.init(false, pair.getPublic());
boolean valid = signer.verifySignature("Message".getBytes(), signature[0], signature[1]);
System.out.println("Подпись верна: " + valid);
4.7. org.bouncycastle.crypto.encodings.PKCS1Encoding
Сигнатура: public class PKCS1Encoding implements AsymmetricBlockCipher
Что делает: Добавляет к RSA шифрованию padding по стандарту PKCS#1 v1.5 (или OAEP). Предотвращает атаки на "сырой" RSA.
Пример:
RSAEngine rsaEngine = new RSAEngine();
PKCS1Encoding cipher = new PKCS1Encoding(rsaEngine);
cipher.init(true, new ParametersWithRandom(publicKey, random)); // Шифрование
byte[] encrypted = cipher.processBlock(plaintext, 0, plaintext.length);
cipher.init(false, privateKey); // Дешифрование
byte[] decrypted = cipher.processBlock(encrypted, 0, encrypted.length);
4.8. org.bouncycastle.crypto.macs.HMac
Сигнатура: public class HMac implements Mac
Что делает: Вычисляет HMAC (Hash-based Message Authentication Code) на основе любого дайджеста (SHA-256, SHA-3 и т.д.). Используется для проверки целостности и аутентичности сообщения.
Пример:
HMac hmac = new HMac(new SHA256Digest());
KeyParameter keyParam = new KeyParameter(keyBytes);
hmac.init(keyParam);
byte[] data = "Authenticate me".getBytes();
hmac.update(data, 0, data.length);
byte[] mac = new byte[hmac.getMacSize()];
hmac.doFinal(mac, 0);
System.out.println("HMAC: " + Hex.toHexString(mac));
5. Реальный практический пример
Разработаем утилиту для безопасного хранения и верификации паролей с использованием Argon2id и AES-GCM для шифрования "секретов" пользователя. Пример демонстрирует комбинацию хеширования паролей и симметричного шифрования.
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.util.encoders.Hex;
import java.security.SecureRandom;
import java.util.Arrays;
public class SecureVault {
private static final int SALT_LENGTH = 16;
private static final int IV_LENGTH = 12;
private static final int KEY_LENGTH = 32; // 256-bit
// Хеширование пароля с помощью Argon2id
public static byte[] hashPassword(char[] password, byte[] salt) {
Argon2BytesGenerator gen = new Argon2BytesGenerator Также в библиотеке
Работа с XML в Swift: парсинг, создание и обработка данных (iOS/macOS)
HTTP-клиент для JavaScript (Node.js): топ библиотек
Работа с Markdown в Go (Golang): библиотеки, парсинг и конвертация
Glide/Coil для Kotlin: полное руководство с примерами
Машинное обучение на JavaScript: библиотеки для Node.js — обзор и примеры