410013796724260
• Webmoney
R335386147728
Z369087728698
Цифровая подпись jar файловС 2014 г. для запуска приложения Java в веб-браузере необходимо получить цифровой сертификат и «подписать» код программы. Это относится к размещаемым на web-странице приложениям типа апплет, выполненным в виде jar файла. Но, если можно подписать апплет, то, следовательно, можно подписать и обычное десктопное (desktop) Java-приложение, выполненное в виде jar. И не обязательно, чтобы этот jar был исполняемым; он может выполнять функции java-библиотеки. Поскольку java-приложения и java-библиотеки могут распространяться в виде готовых jar-модулей, то, естественно, некоторые разработчики заинтересованы, чтобы их код не был модифицирован. Те разработчики, которые разрешают вносить изменения в свой код, распространяют приложения с открытым кодом под определенной лицензией. Но, вот закрытый код в виде jar-файла следует каким-либо образом защитить; это можно сделать с использованием цифровой подписи. Конечно, цифровая подпись не защитит код от взлома – файл можно декомпилировать, внести изменения и снова собрать. Но после таких махинаций цифровая подпись станет недействительной, и автор может либо предъявить претензии, либо откреститься от непредусмотренных кодом действий. То есть, автор не будет нести ответственность перед пользователем за модифицированное приложение. В данной статье будет рассмотрен вопрос цифровой подписи jar файлов. Самоподписанный сертификатДля подписи jar файла будем использовать утилиту jarsigner, которая входит в комплект поставки JDK (Java Development Kit). Первое, что нам нужно – это получить цифровой сертификат. Мудрить долго не будем и воспользуемся самоподписанным сертификатом, который сами же и создадим с помощью утилиты keytool. О том, как создать самоподписанный сертификат, а также опции утилиты keytool подробно описано здесь. Не будем повторяться и выполним следующую команду : C:\Program Files\Java\jdk1.8.0_161\bin>keytool \ -v -genkey -dname "CN=java-online.ru, OU=Developers, \ O=IT Systems Inc., L=Moscow, C=RF" -alias codesigner \ -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] Warning: The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.jks -deststoretype pkcs12". При выполнении данной команды будет создано хранилище keystore.jks с сертификатом, алиас которого 'codesigner'. Если хранилище keystore.jks было создано ранее, то в него будет добавлен новый сертификат. Для доступа к хранилищу и сертификату определены соответствующие пароли : mystorepass и mykeypass. Проект приложенияВ качестве примера для тестирования подписи jar файла создадим проект certificate-reader в IDE Eclipse, представленный на следующем скриншоте. В проект включим класс CertificateReader, позволяющий выполнять чтение сертификатов хранилища. Описание данного класса приведено здесь. Поэтому, на описании и функциональных возможностях класса останавливаться не будем, а сразу же создадим исполняемый модуль certificate-reader.jar, который будем «подписывать». В проект включены три командных bat-файла, которые можно использовать для старта самого приложения из командной строки, подписи файла и проверки цифровой подписи файла, а также хранилище keystore.jks с самоподписанным сертификатом. Файл MANIFEST.MF до подписиПервое, на что следует обратить внимание в jar файле, так это на содержимое манифеста META-INF/MANIFEST.MF, который включает только наименование основного класса, имеющего метод main для старта приложения : Manifest-Version: 1.0 Class-Path: . Main-Class: com.labir.CertificateReader Примечание : jar-файл – это архив, который можно открыть любым из архиваторов, например 7-zip. Кроме этого содержимое архива включает директорию META-INF с файлом манифеста MANIFEST.MF. Утилита формирования подписи, jarsignerПосле того, как jar файл подготовлен, переходим к подписи файла с использованием утилиты jarsigner, входящей в комплект JDK. Утилита jarsigner является инструментом для решения двух задач :
Цифровая подпись представляет строку битов, которая вычисляется из некоторых данных ("подписываемые" данные) с помощью закрытого ключа. Отличительные характеристики цифровой подписи :
Чтобы генерировать цифровые подписи jar файлов jarsigner использует ключ и сертификат хранилища, который является базой данных закрытых ключей, аутентифицирующих соответствующие открытые ключи. Для создания и администрирования хранилища используется утилита keytool. Файл jar с цифровой подписью включает копию сертификата от хранилища для открытого ключа, соответствующего закрытому ключу, используемому для подписи файла. Утилита jarsigner проверяет цифровую подпись файла jar, используя размещенный внутри файла сертификат. Команда подписи файла jarПри подписи файла утилите jarsigner в качестве опций необходимо указать хранилище ключей и сертификатов -keystore, пароль хранилища -storepass и пароль закрытого ключа -keypass, файл jar и псевдоним сертификата. Следующая команда сформирует цифровую подпись файла certificate-reader.jar и разместит ее внутри архива : >"C:/Program Files/Java/jdk1.8.0_161/bin/"jarsigner.exe \ -verbose -keystore keystore.jks -storepass mystorepass \ -keypass mykeypass certificate-reader.jar codesigner Java(TM) SE Runtime Environment (build 1.8.0_161-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode) updating: META-INF/MANIFEST.MF adding: META-INF/CODESIGN.SF adding: META-INF/CODESIGN.RSA adding: com/ adding: com/labir/ signing: com/labir/CertificateReader$1.class signing: com/labir/CertificateReader$2.class signing: com/labir/CertificateReader.class jar signed. Warning: No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2019-04-16) or after any future revocation date. При формировании цифровой подписи jarsigner вывел в консоль дополнительную информацию, связанную с указанием внесения изменений в файл META-INF/MANIFEST.MF и добавлением двух файлов (META-INF/CODESIGN.SF, META-INF/CODESIGN.RSA). Далее приводятся наименования файлов (классов), для которых сформированы подписи. Обратите внимание, что алиас сертификата 'codesigner' в наименованиях добавленных файлов сокращен до восьми символов CODESIGN. MANIFEST.MF после подписиПосле подписи jar файла содержимое манифеста изменилось добавлением 3-х дайджестов : Manifest-Version: 1.0 Class-Path: . Main-Class: com.labir.CertificateReader Name: com/labir/CertificateReader$2.class SHA-256-Digest: dMd0CV9qRF4KUznsnNFo1slW0g6U1zwRn3jM4PVjjw8= Name: com/labir/CertificateReader$1.class SHA-256-Digest: UhMt98YW190OqDiV1NExH4eCMZtzto0vjzEnPpME9rg= Name: com/labir/CertificateReader.class SHA-256-Digest: jtwnwiYnLa+qY3F2N5105qa+oWM/FNonBtBQtal+tx8= CODESIGN.SFФайл CODESIGN.SF содержит дайджесты манифеста и файлов архива jar. Signature-Version: 1.0 SHA-256-Digest-Manifest-Main-Attributes: QjbCmYJbFmnmqyFWabrHW2Jd7kwYt GnzIHhXpBF4UWM= SHA-256-Digest-Manifest: AfgfUrLzD7tMXju3Io/DU0iTeRMoCgybFeV/J4fVGO0= Created-By: 1.8.0_161 (Oracle Corporation) Name: com/labir/CertificateReader$2.class SHA-256-Digest: neEYk0uhwtFOKOk/Vr3pGBDnzGn0qahBg8TupL4M7tI= Name: com/labir/CertificateReader$1.class SHA-256-Digest: BqfYq26UnpZjz9thlc+oDbuijR35VSiwnbnlVl+z/qM= Name: com/labir/CertificateReader.class SHA-256-Digest: C4vnZjuC2OYkg7qMAPjoMhdsLZJDq6YyHvrqHWWjThA= CODESIGN.RSAФайл CODESIGN.RSA содержит цифровой сертификат RSA, используемый в криптографии с открытым ключом. Проверка подписи файла с использованием jarsignerЧтобы выполнить проверку цифровой подписи jar файла с помощью утилиты jarsigner необходимо использовать опцию -verify. Следующая команда демонстрирует проверку «действительной» подписи файла certificate-reader.jar. > "C:/Program Files/Java/jdk1.8.0_161/bin/"jarsigner.exe \ -verify -verbose -certs certificate-reader.jar Java(TM) SE Runtime Environment (build 1.8.0_161-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode) s 400 Mon Apr 16 12:00:56 MSK 2018 META-INF/MANIFEST.MF X.509, CN=java-online.ru, OU=Developers, \ O=IT Systems Inc., L=Moscow, C=RF [certificate is valid from 16.04.18 12:00 \ to 16.04.19 12:00] [CertPath not validated: Path does not chain \ with any of the trust anchors] 550 Mon Apr 16 12:00:56 MSK 2018 META-INF/CODESIGN.SF 1352 Mon Apr 16 12:00:56 MSK 2018 META-INF/CODESIGN.RSA 0 Mon Apr 16 11:07:32 MSK 2018 com/ 0 Mon Apr 16 11:07:32 MSK 2018 com/labir/ sm 765 Mon Apr 16 11:08:38 MSK 2018 \ com/labir/CertificateReader$1.class X.509, CN=java-online.ru, OU=Developers, O=IT Systems Inc., \ L=Moscow, C=RF [certificate is valid from 16.04.18 12:00 to 16.04.19 12:00] [CertPath not validated: Path does not chain with any of the \ trust anchors] sm 1424 Mon Apr 16 11:08:38 MSK 2018 \ com/labir/CertificateReader$2.class X.509, CN=java-online.ru, OU=Developers, O=IT Systems Inc., \ L=Moscow, C=RF [certificate is valid from 16.04.18 12:00 to 16.04.19 12:00] [CertPath not validated: Path does not chain with any of the \ trust anchors] sm 8177 Mon Apr 16 11:08:38 MSK 2018 \ com/labir/CertificateReader.class X.509, CN=java-online.ru, OU=Developers, O=IT Systems Inc., \ L=Moscow, C=RF [certificate is valid from 16.04.18 12:00 to 16.04.19 12:00] [CertPath not validated: Path does not chain with any of the \ trust anchors] s = signature was verified m = entry is listed in manifest k = at least one certificate was found in keystore i = at least one certificate was found in identity scope - Signed by "CN=java-online.ru, OU=Developers, O=IT Systems Inc., \ L=Moscow, C=RF" Digest algorithm: SHA-256 Signature algorithm: SHA256withRSA, 2048-bit key jar verified. Warning: This jar contains entries whose certificate chain is not validated. This jar contains signatures that does not include a timestamp. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2019-04-16) or after any future revocation date. В случае, если цифровая подпись окажется «недействительной», то можно увидеть следующее сообщение : > "C:/Program Files/Java/jdk1.8.0_161/bin/"jarsigner.exe \ -verify -verbose -certs certificate-reader.jar Java(TM) SE Runtime Environment (build 1.8.0_161-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode) jarsigner: java.lang.SecurityException: invalid SHA-256 signature \ file digest for com/labir/CertificateReader$1.class Проверка «недействительной» подписи заканчивается вызовом исключения типа SecurityException. Программная проверка подписиВыполним проверку цифровой подписи файла программным способом. Для этого необходимо только прочитать файлы архива jar. Если один из файлов будет изменен и его подпись будет недействительной, то чтение завершится вызовом исключения SecurityException. Следующий пример демонстрирует проверку подписанного файла. import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class SignVerify { private final String jar_path = "D:/certificate-reader.jar"; SignVerify () { try { JarFile jfile = new JarFile(jar_path); System.out.println("verify = " + jarVerify(jfile)); } catch (IOException e) {} } private static boolean jarVerify(JarFile jar) throws IOException { Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); System.out.println("entry.name = " + entry.getName()); try { byte[] buffer = new byte[16384]; InputStream is = jar.getInputStream(entry); while ((is.read(buffer,0,buffer.length)) != -1){ // Только чтение, которое может вызвать // SecurityException, если цифровая подпись // нарушена. } } catch (SecurityException se) { System.err.println("SecurityException : " + se.getMessage()); return false; } } return true; } public static void main(String[] args) throws IOException { new SignVerify(); System.exit(0); } } При чтении архивного файла в консоль выведена следующая информация : entry.name = com/ entry.name = com/labir/ entry.name = com/labir/CertificateReader$1.class entry.name = com/labir/CertificateReader$2.class entry.name = com/labir/CertificateReader.class entry.name = META-INF/CODESIGN.RSA entry.name = META-INF/CODESIGN.SF entry.name = META-INF/MANIFEST.MF verify = true Если подпись файла окажется недействительной, то в консоли будет выведено сообщение об ошибке. Чтобы убедиться в этом достаточно внести изменение в один из дайджестов манифеста и снова запустить пример. В примере можно организовать дополнительную проверку, связанную, например, с сертификатом (файлом .RSA). Здесь все зависит от Вашего опыта и желания блокировать выполнение измененного приложения/библиотеки. Скачать примерИсходный код рассмотренного на странице примера подписи jar файла с использованием утилиты jarsigner в виде проекта IDE Eclipse можно скачать здесь (21.2 Kб). |