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 Кб). |