410013796724260
• Webmoney
R335386147728
Z369087728698
Хранилище ключей и сертификатовЗащита данных в приложениях имеет важное значение, защита конфиденциальной информации — первостепенное. Одним из самых распространённых способов защиты информации во все времена является шифрование данных. Криптография, симметричное и асимметричное шифрование, ключи и сертификаты непосредственно связаны с данной задачей. Используемые для защиты информации ключи и сертификаты также нужно надежно защитить. Для этих целей используется keystore — хранилище сертификатов и ключей. keystore — это специализированное хранилище секретных данных, которое используется Java-приложениями для шифрования, аутентификации и установки HTTPS соединений. Так, для аутентификации клиента и сервера, устанавливающих SSL (Secure Sockets Layer — уровень защищённых cокетов) соединение, требуются приватные ключи и сертификаты. Если используется односторонняя аутентификация, то keystore используется только на серверной стороне. При двусторонней аутентификации клиент и сервер обмениваются сертификатами; соответственно и у сервера, и у клиента должны быть keystore с парой ключей private/public + сертификат. Иными словами keystore используется для хранения ключей и сертификатов, применяемых для идентификации владельца ключа (клиента или сервера). Java поддерживает несколько форматов хранилищ keystore :
Каждая запись в keystore имеет уникальный псевдоним (alias). Рекомендуется в keystore не использовать alias'ы, отличающиеся только регистром. В стандартной реализации каждый ключ в хранилище защищается паролем; кроме того, всё хранилище целиком может быть защищено отдельным паролем. Стандартное хранилище доверенных CA-сертификатов (Certificate Authority) для Java приложений располагается в директории jre/lib/security/cacerts (пароль - changeit). Информацию в хранилище можно разделить на две категории: ключевые записи (пары ключей private/public) и доверенные сертификаты. Ключевая запись, используемая для криптографических целей, включает идентификационные данные объекта и его закрытый ключ. Доверенный сертификат содержит идентификационные данные объекта и открытый ключ. Запись с доверенным сертификатом не может использоваться в тех случаях, где требуется закрытый ключ. Чтобы отделить ключевые записи от сертификатов целесообразно использовать различные хранилища : один для собственных ключей, а другой — для доверенных сертификатов, включая сертификаты Центров сертификации (CA). Такой подход позволит реализовать разделение между собственными сертификатами и соответствующими закрытыми ключами, и доверенными сертификатами. Дополнительно можно обеспечить более высокую защиту для закрытых ключей в отдельном keystore с ограниченным доступом, а доверенные сертификаты оставить в более свободном доступе. В конце статьи представлен Java пример просмотра содерживого хранилища ключей и сертификатов CertificateReader. Утилита keytoolДля управления парами ключей (private/public), сертификатами и хранилищем keystore Java включает утилиту keytool, располагаемую в директории bin. Для запуска keytool можно использовать командную строку. Опции утилиты позволяют выполнять различные операции и получать определенные сведения. Так, чтобы получить информацию об утилите, можно просто выполнить команду keytool без опций : C:\Program Files\Java\jre1.8.0_121\bin>keytool Key and Certificate Management Tool Commands: -certreq Generates a certificate request -changealias Changes an entry's alias -delete Deletes an entry -exportcert Exports certificate -genkeypair Generates a key pair -genseckey Generates a secret key -gencert Generates certificate from a certificate request -importcert Imports a certificate or a certificate chain -importpass Imports a password -importkeystore Imports one or all entries from another keystore -keypasswd Changes the key password of an entry -list Lists entries in a keystore -printcert Prints the content of a certificate -printcertreq Prints the content of a certificate request -printcrl Prints the content of a CRL file -storepasswd Changes the store password of a keystore Use "keytool -command_name -help" for usage of command_name Чтобы получить дополнительную справку о команде необходимо указать ее наименование и волшебное слово help. Не забывайте о дефисе '-' перед опциями : C:\Program Files\Java\jre1.8.0_121\bin>keytool -certreq -help keytool -certreq [OPTION]... Generates a certificate request Options: -alias <alias> alias name of the entry to process -sigalg <sigalg> signature algorithm name -file <filename> output file name -keypass <arg> key password -keystore <keystore> keystore name -dname <dname> distinguished name -storepass <arg> keystore password -storetype <storetype> keystore type . . . -providerarg <arg> provider argument -providerpath <pathlist> provider classpath -v verbose output -protected password through protected mechanism Use "keytool -help" for all available commands Создание самоподписанного сертификатаДля создания самоподписанного сертификата также необходимо использовать команду -genkey с указанием срока действия сертификата в опции -validity. Следующая команда создаст пару 2048-битных RSA-ключей, действительных на протяжении 365 дней, с указанным псевдонимом (parent) в заданном файле/хранилище ключей (keystore.jks). Закрытый ключ в хранилище «закрывается» паролем, открытый ключ «оборачивается» в самоподписанный сертификат.
keytool -genkey -alias parent -keyalg RSA -validity 365 \
-keystore keystore.jks
Если заданного хранилища ключей (keystore.jks) не существует, то keytool создаст его. При выполнении команды keytool будет запрашивать некоторые необходимые данные : пароль хранилища, Distinguished Name и пароль закрытого ключа. Многие параметры используются со значениями по умолчанию. Так, например, алиас - mykey, хранилище - .keystore в домашней директории пользователя (HOMEPATH), алгоритм шифрвания - SHA1withDSA и пр. Distinquished NameСертификат создается в формате X.509. В этом формате в качестве идентификатора владельца используется Distinquished Name или просто DN в формате X.500. Этот же формат идентификации объектов используется в LDAP-протоколе или в SNMP. Distinquished Name задается в виде разделенных через запятую атрибутов :
Часть из атрибутов могут быть пропущены; в этом случае им будет присвоено значение Unknown. Одним из важных атрибутов сертификата являются альтернативные имена SAN (SubjectAlternativeName). Подробности и пример внесения SAN в самоподписанный сертификат представлены на странице настройки конфигурации сервера Tomcat. Параметры сертификата можно указывать в качестве опции команды. Так, в следующем варианте примера задаётся информация о компании, наименование алиаса, тип и размещение хранилища, срок действия, алгоритм для генерации ключей, размер ключа, пароли на хранилище и на ключ.
C:\Program Files\Java\jdk1.8.0_121\bin> \
keytool -v -genkey -dname "CN=java-online.ru, \
OU=Developers, O=IT Systems Inc., L=Moscow, C=RF" \
-alias parent -storetype jks -keystore keystore.jks \
-validity 365 -keyalg RSA -keysize 2048 \
-storepass mystorepass -keypass mykeypass
Generating 2 048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 365 days
for: CN=java-online.ru, OU=Developers, O=IT Systems Inc., L=Moscow, C=RF
[Storing keystore.jks]
Новое хранилище было размещено в той же директории, где и располагается keytool. Создание пары ключейДля создания пары ключей необходимо использовать команду "-genkeypair". Следующая команда создаст пару ключей "keypair" в хранилище keystore.jks, где размещен созданный ранее сертификат.
C:\Program Files\Java\jdk1.8.0_121\bin> \
keytool -alias keypair -genkeypair -keystore keystore.jks \
-dname "CN=java-online.ru"
Enter keystore password:
Enter key password for <keypair>
(RETURN if same as keystore password):
Сертификат и закрытый ключ сохранены в виде новой keystore записи, идентифицированной псевдонимом "keypair". Открытый ключ обертывается в формат X.509 — это самоподписанный сертификат, который сохранен как одноэлементная цепочка сертификата. Опции команды genkeypair
Создадим еще две пары ключей с псевдонимами "keypair1" и "keypair2", чтобы при просмотре содержимого хранилища (ниже) был небольшой список пар ключей :
keytool -alias keypair1 -genkeypair -keystore keystore.jks \
-dname "CN=java-online.ru"
keytool -alias keypair2 -genkeypair -keystore keystore.jks \
-dname "CN=java-online.ru"
Экспорт сертификатаСертификат можно экспортировать из хранилища и предоставить его пользователям Вашей «подписанной» программы. Тогда пользователи могут занести Ваш сертификат в свое хранилище доверенных сертификатов. Для экспорта сертификата используется команда "exportcert". Следующий пример извлекает из хранилища сертификат в файл "parent.cer" : C:\Program Files\Java\jdk1.8.0_121\bin> \ keytool -exportcert -keystore keystore.jks \ -alias parent -file parent.cer Enter keystore password: Certificate stored in file <parent.cer> Импорт сертификатаЧтобы импортировать сертификат в хранилище, нужно его сначала получить каким-либо образом. Не будем мудрить и извлечем сертификат с псевдонимом veriSignclass1g3ca из хранилища доверенных сертификатов jre\lib\security\cacerts (пароль хранилища changeit). То есть выполним команду экспорта сертификата с указанием соответствующего хранилища : Экспорт сертификата из хранилища cacertsC:\Program Files\Java\jdk1.8.0_121\bin> keytool -exportcert -alias veriSignclass1g3ca -keystore \ "C:\Program Files\Java\jdk1.7.0_67\jre\lib\security\cacerts" \ -file veriSignclass1g3ca.cer Enter keystore password: Certificate stored in file <veriSignclass1g3ca.cer> Импорт сертификата в хранилищеЧтобы импортировать сертификат в хранилище keystore.jks необходимо использовать команду "importcert". Если в качестве опции указать "-trustcacerts", то сертификат импортируется в хранилище доверенных сертификатов, т.е. в jre\lib\security\cacerts. При выполнении команды импорта утилита keytool попросит ввести пароль хранилища :
C:\Program Files\Java\jdk1.8.0_121\bin> \
keytool -importcert -keystore keystore.jks \
-file veriSignclass1g3ca.cer
Enter keystore password: \
Owner: \
CN=VeriSign Class 1 Public Primary Certification Authority - G3, \
OU="(c) 1999 VeriSign, Inc. - For authorized use only", \
OU=VeriSign Trust Network, \
O="VeriSign, Inc.", \
C=USIssuer: \
CN=VeriSign Class 1 Public Primary Certification Authority - G3, \
OU="(c) 1999 VeriSign, Inc. - For authorized use only", \
OU=VeriSign Trust Network, \
O="VeriSign, Inc.", \
C=US \
Serial number: 8b5b75568454850b00cfaf3848ceb1a4 \
Valid from: \
Fri Oct 01 04:00:00 MSD 1999 until: Thu Jul 17 02:59:59 MSK 2036 \
Certificate fingerprints: \
MD5: B1:47:BC:18:57:D1:18:A0:78:2D:EC:71:E8:2A:95:73 \
SHA1: 20:42:85:DC:F7:EB:76:41:95:57:8E:13:6B:D4:B7:D1:E9:8E:46:A5 \
SHA256: CB:B5:AF:18:5E:94:2A:24:02:F9:EA:CB:C0:ED:5B:B8:76:EE:A3: \
C1:22:36:23:D0:04:47:E4:F3:BA:55:4B:65
Signature algorithm name: SHA1withRSA Version: 1 \
Trust this certificate? [no]: y
Certificate was added to keystore
Просмотр хранилищаДля чтения содержимого хранилища необходимо использовать команду "-list". В качестве опции "-keystore" можно указать путь к хранилищу. По умолчанию команда "-list" отображает цифровой отпечаток SHA1 сертификата. Следующий код позволяет просмотреть содержимое созданного хранилища, включающего сертификат и три пары ключей :
C:\Program Files\Java\jdk1.8.0_121\bin> \
keytool -list -keystore keystore.jks
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 5 entries
keypair2, 14.02.2018, PrivateKeyEntry,
Certificate fingerprint (SHA1): \
C4:02:BA:D7:24:6B:84:2F:CD:F9:81:16:5F:74:E0:31:7B:C0:19:B1
keypair1, 14.02.2018, PrivateKeyEntry,
Certificate fingerprint (SHA1): \
AB:BA:92:77:44:BD:B0:65:EB:29:0C:F9:86:64:0F:81:B7:4A:27:9A
keypair, 14.02.2018, PrivateKeyEntry,
Certificate fingerprint (SHA1): \
8A:8B:21:83:1E:75:4F:C7:62:85:6A:31:84:45:AA:16:2B:20:06:1E
parent, 13.02.2018, PrivateKeyEntry,
Certificate fingerprint (SHA1): \
DB:8B:9D:9D:DF:5B:B3:82:0E:19:C6:A4:A4:3E:08:C0:AB:20:F9:85
mykey, 18.02.2018, trustedCertEntry,
Certificate fingerprint (SHA1): \
20:42:85:DC:F7:EB:76:41:95:57:8E:13:6B:D4:B7:D1:E9:8E:46:A5
Опции команды list
Если при просмотре хранилища использовать опцию "-v", то информация о сертификате выводится с дополнительной информацией, включающей владельца, порядковый номер и т.д. При использовании опции "-rfc" содержание сертификата печатается согласно интернет-стандарта RFC-1421. На странице описания SSL сертификата представлен результат выполнения команды просмотра хранилища keytool -list для опций '-v' и '-rfc'. Полную англоязычную версию документации на keytool можно найти здесь. Пример просмотра хранилища и сертификатаНа скриншоте представлен пример CertificateReader, позволяющий просматривать хранилище ключей и сертификаты, а также извлекать информацию о сертификате.
Внутри сертификата хранится пара значений Distinqueshed Names. Один DN принадлежит владельцу сертификата, а второй DN указывает идентификатор цента сертификации (CA), подписавшего сертификат. В случае с самоподписанным (self-signed) сертификатом, оба эти DN указывают на владельца сертификата. Листинг примераИнтерфейс примера выполнен с использованием библиотеки Swing. Поскольку Swing не является предметом данной статьи, то методы формирования интерфейса не представлены в листинге примера. При необходимости желающие могут скачать исходный код примера в конце страницы. В листинге примера представлены два метода : loadKeyStore, showCertificate. Первый метод позволяет выбрать хранилище сертификатов. Второй метод выполняет чтение сертификата и представление его параметров в интерфейсе. После листинга представлен скриншот, на котором выполнено чтение созданного сертификата.
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
public class CertificateReader extends JFrame
{
final String TERMIN = "Срок действия сертификата%s";
final String VALID = "действителен" ;
final String INVALID = "не действителен" ;
final String CREATER = "Издатель%s" ;
final String NUMBER = "Серийный номер%s" ;
final String START = "Начало срока действия%s" ;
final String END = "Конец срока действия%s" ;
final String OWNER = "Владелец%s" ;
final String ALGORITM = "Алгоритм подписи%s" ;
final String SIGN = "Подпись сертификата%s" ;
final String LF = "\n" ;
final String LF_SPACE = " :\n " ;
KeyStore keyStore = null; // хранилище
JList<String> lstAliases = null;
JTextField txtFileName = null;
JTextArea taCertificate = null;
final int LIST_size = 140 ;
public CertificateReader()
{
setTitle("Просмотр хранилища сертификатов");
setSize(600, 480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(createCtrl(), BorderLayout.SOUTH);
getContentPane().add(createGUI (), BorderLayout.CENTER);
setVisible(true);
}
private JPanel createCtrl()
{
. . .
}
private JSplitPane createGUI()
{
. . .
}
void loadKeyStore()
{
FileInputStream fis;
// Выбор хранилища сертификатов
JFileChooser chooser = new JFileChooser();
if(chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
txtFileName.setText(chooser.getSelectedFile().getAbsolutePath());
try {
// Чтение хранилище сертификатов
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
fis = new FileInputStream(txtFileName.getText());
keyStore.load(fis, null);
Enumeration<String> E = keyStore.aliases();
// Формирование набор сертификатов
Vector<String> certs = new Vector<String>();
while (E.hasMoreElements())
certs.add( (String)E.nextElement() );
// Размещение сертификатов в компоненте
lstAliases.setListData(certs);
invalidate();
}
catch (Exception e) {
JOptionPane.showMessageDialog(this ,
"Ошибка чтения хранилища сертификатов:\n" + e);
}
}
}
void showCertificate(final String name)
{
Certificate cert = null;
try {
// Чтение сертификата
cert = keyStore.getCertificate(name);
X509Certificate xcert = (X509Certificate) cert;
String valid = "";
try {
xcert.checkValidity();
valid = VALID;
} catch (Exception ex){
valid = INVALID;
}
SimpleDateFormat sdf;
sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
String end = sdf.format(xcert.getNotAfter ());
String start = sdf.format(xcert.getNotBefore());
String sign = new sun.misc.BASE64Encoder()
.encode(cert.getSignature());
String creater = xcert.getIssuerDN().getName();
String owner = xcert.getSubjectDN().getName();
String number = String.valueOf(xcert.getSerialNumber());
String algo = xcert.getSigAlgName();
String info;
info = createLine(TERMIN , valid );
info += createLine(CREATER , creater);
info += createLine(NUMBER , number );
info += createLine(START , start );
info += createLine(END , end );
info += createLine(OWNER , owner );
info += createLine(ALGORITM, algo );
info += createLine(SIGN , sign );
taCertificate.setText(info);
} catch (KeyStoreException ex1) {
JOptionPane.showMessageDialog(this ,
"Ошибка получения из хранилища сертификата с " +
псевдонимом <<lstAliases.getSelectedValue()+">>");
}
}
protected String createLine (String templ, String text)
{
return String.format(templ, LF_SPACE + text + LF);
}
public static void main(String[] args) {
new CertificateReader();
}
}
Примечание : класс CertificateReader используется в качестве примера на странице цифровой
подписи jar файлов Скачать примерРассмотренный на странице пример просмотра хранилища ключей и сертификатов можно скачать здесь (2.5 Кб). |
