Ключи, подписи, сертификаты, шифрование, ...

Создан: 19.07.2011
Модиф: 16.08.2011
Иванов Аркадий

 

Для безопасной передачи данных через небезопасный Интернет придумано многое - GnuPG, OpenSSL, Java SSL,.... Что использовать - определяется задачей и разработчиком программ.

В этом описании я немного соберу для самого себя команды, которые приходится использовать для создания ключей шифрования и всего, что с ними связано.

 

Важнейшим математическим трюком, который используется при шифровании, является следующий:

  • Сторона A создаёт пару ключей. Ключ - это длинная последовательность битов, например 256 бит. В созданной паре один ключ является закрытым (private) ключём, который бережно хранится владельцем. Второй ключ является открытым (public), т.е. его совершенно спокойно можно передать по доступным врагам каналам своему товарищу.
  • Прелесть ключей и алгоритмов шифрования состоит в том, что когда сторона A создала эту пару ключей и передала открытый ключ стороне B, теперь сторона B может шифровать свои сообщения для A открытым ключём и посылать их к A через Интернет.
  • Чтобы расшифровать сообщение от B, сторона А  использует свой закрытый ключ.
  • Хакерам позволено перехватить весь поток данных между A и B, как открытый ключ, так и зашифрованные им сообщения. Алгоритм,  с помощью которого шифрует сторона B, весьма сложен и не позволяет перехватчикам подобрать его за разумное время без наличия у них закрытого ключа.
  • Вот именно этот алгоритм и является достижением математиков, позволившим передавать безопасно данные через Интернет между компьютерами без предварительной передачи ключей с помощью курьера или каким-нибудь другим неэлектронным образом.

 Выше речь шла о шифровании сообщений.

 

Наличие таких алгоритмов позволило реализовать ещё одну важную функцию - электронную подпись. Электронная подпись - это набор символов, который прицепляется к сообщению или идет отдельно от сообщения и позволяет получателю твёрдо удостоверится, что сообщение пришло от нужного автора. Здесь тоже используется пара ключей.

  • В случае с подписью сторона А создаёт private и public ключи. Публичный ключ она по традиции пересылает стороне B.
  • Сторона А создаёт сообщение и добавляет к нему электронную подпись с помощью private ключа. В создании подписи участвует как сам текст всего сообщения, так и закрытый ключ.
  • Сторона B получает сообщение по открытым каналам и с помощью ранее полученного public (открытого) ключа A проверяет, что сообщение действительно пришло от A.

 

Замечу, что для шифрования сообщения на стороне B используется открытый ключ А, и сторона А будет расшифровывать присланное сообщение своим закрытым ключём.

Но для создания подписи используется свой закрытый ключ и другая сторона будет удостоверяться в авторстве полученного послания с помощью открытого ключа другой стороны.

 

Эти механизмы позволяют очень чётко разграничить ответственность получателя и посылателя. Если вы получили правильную подпись от получателя, даже если позже выяснилось, что это сообщение было сфабриковано, ответственность лежит на отправителе, поскольку у вас нет возможности подделать его подпись - у вас нет закрытого ключа.

 

При передаче открытого ключа другой стороне обычно дополнительно передаётся информация о сервере, чёй это открытый ключ. В файле, где лежит открытый ключ, лежит и инфо о владельце.

 

Открытый ключ + доп. информация о владельце называется сертификатом.

При создании ключей обычно сразу создаётся закрытый ключ и сертификат.

 

Сертификаты могут быть подписаны электронной подписью Центра Сертификации (Center of Athority - CA). Обычно CA - это  третья сторона,  которая такой подписью удостоверяет, что это действительно сертификат вашей компании. Создав закрытый ключ и сертификат, вы просите СА подписать свой сертификат их подписью. Программы для создания ключей в таких случаях создают не сертификат, а CSR - Certificate Signing Request, запрос на подпись сертификата, который вы отсылаете в CA. Прежде, чем подписать ваш сертификат, третья сторона проводит исследования, что вы - это вы, а не жулик. Открытые ключи этой третьей стороны обычно уже содержатся в программах, например в броузерах. При установлении зашифрованного соединения броузер получает от вашего сервера подписанный третьей стороной сертификат (т.е. открытый ключ вашего сервера + информацию о вас + электронную подпись от CA). Имея открытый ключ самого CA, броузер проверяет правильность электронной подписи. Если подпись не подделка, браузер без лишних вопросов начинает использовать открытый ключ вашего сервера. Несколько фирм в мире ведут свой бизнес на том, что подписывают сертификаты, например,Twahte, VeriSign.

 

Для SSL-соединений используются сертификаты с подписью. Иногда можно использовать и самоподписанные сертификаты, т.е. в качестве CA используется ваша фирма.

Получая такой самоподписанный сертификат от сервера броузеры спрашивают пользователя о его согласии продолжать работать и позволяют добавить ваш сертификат в список тех, кому вы доверяете. Для нормального Internet-бизнеса использование самоподписанных сертификатов опасно, поскольку позволяет хакерам сделать копию вашего сайта и отловить данные невнимательных пользователей.

 

Теперь перехожу к командам разных систем для работы с ключами.

Создание пары ключей:

 

OpenSSL

openssl req -new -x509 -days 3660 -newkey rsa:2048 -keyout privkey.pem -out pubkey.crt \
-subj "/C=RU/ST=Kamchatka/L=Elizovo/O=Roga I Kopyta/OU=IT/CN=www.mydomain.ru/emailAddress=my@addr.com"


Команда создаст файлы privkey.pem - закрытый ключ и pubkey.crt - открытый ключ (точнее сертификат), ещё и пароль задаст.


req

    запрос на создание нового сертификата

-new

    Создание запроса на сертификат (Certificate Signing Request (CSR)).

-newkey rsa:2048

    cоздать новый закрытый RSA ключ длиной 2048 бита.

-keyout privkey.pem

    Файл закрытого ключа - privkey.pem

-x509

    Вместо создания CSR (см. опцию -new) создать самоподписанный сертификат.

-days 3660
    Срок действия сертификата 3600 дней.

-subj "/C=RU/ST=Kamchatka/L=Elizovo/O=Roga I Kopyta/OU=IT/CN=www.mydomain.ru/emailAddress=my@addr.com"

    Данные сертификата, пары параметр=значение, перечисляются через '/'. Если в параметре есть пробел, его можно   

    замаскировать с помощью обратного слэша "\", например "O=Roga\ I\  Kopyta".

    Описание параметров:
    С - код страны (Country).
    ST - Название региона,области,края
    L - Название города,поселка (Locality Name)
    O - Название организации (Organization Name)

    OU - Название подразделения (Organization Unit).

    CN - доменное имя хоста (Common Name). При создании сертификата для сервера оно должно совпадать с

            DNS-именем сервера.    Это проверяется в SSL-соединении. До 64 символов.
    emailAddress - E-mail адрес.  До 40 символов. 

 

Чтобы снять с ключа пароль, (что для ключа на сервере просто необходимо) делаем следующее:

$ mv privkey.pem privkey.pem.orig
$ openssl rsa -in privkey.pem.orig -out privkey.pem

 GnuPG

Здесь процедура интерактивна:

$ gpg --gen-key

 

Вам надо будет выбрать тип ключа (выбирайте RSA), длину (1024 бита вполне подойдёт), ввести своё имя, E-mail и пароль для ключа (пароль можно пройти Enter-ом, тогда его не будет).

В подкаталоге .gnupg домашнего каталога пользователя программа создаст новые ключи.

Если в диалоге при создании сертификата я указал имя "Arc", то его следует использовать при работе с этими ключами.

  Java

Keytool

 

Диалоговая программа:

$ keytool -genkeypair -alias mysert -keyalg RSA -validity 365

-alias mysert

     В хранилище идентификатором этого сертификата будет "mysert". Работа с сертификатом производится по его алиасу

-keyalg RSA

     Алгоритм, для которого созданы эти ключи, называется RSA.

-validity 365
    Ключи будут действительны 365 дней

В диалоге на вопрос "What is your first and last name?" следует ввести имя сервера.

 

Ключи помещаются в хранилище. По умолчанию это файл в .keystore в домашнем каталоге.

 

 

 

Криптование при помощи открытого ключа:

OpenSSL
openssl rsautl -encrypt -certin -inkey pubkey.pem -in plain.txt -out crypted.txt

rsautl
    использую RSA утилиты
-certin
    указываю, что используется сертификат, а не публичный ключ
-inkey
    указываю файл сертификата
-in 
    указываю файл для зашифровки
-out 
    указываю выходной файл

Замечу, что большие файлы так криптовать не удатся. Поток байтов следует
разбивать на блоки, например, по 100 байтов и криптовать эти блоки по
отдельности.

Если есть открытый ключ, а не сертификат, указывается ключ -pubin,
а не -certin

GnuPG

gpg -r XXX -o 1.encoded --encrypt 1.html

-r XXX

       указываю ID ключа, который надо использовать для шифрования.

-o

       указываю выходной файл

--encrypt

       указываю, что будет операция шифрования

 

 

Расшифровка присланного сообщения:

OpenSSL
openssl rsault -decrypt -certin -inkey privkey.pem -in crypted.txt -out plain.txt
GnuPG gpg -r XXX --out 2 --decrypt 1.encoded

 

 

Создание нового приватного ключа и одновременно с ним CSR:

OpenSSL openssl req -out pubkey.csr -new -newkey rsa:2048 -nodes -keyout privkey.pem

 

 

Создание публичного  ключа из приватного RSA ключа:

OpenSSL
openssl rsa -in privkey.pem -pubout -out pubkey.pem

 

Выделение публичного ключа из сертификата:

OpenSSL openssl x509 -inform pem -in pubkey.crt -noout -pubkey >pubkey.pem

 

 

 

Подпись файла приватным ключом:

OpenSSL
openssl dgst -sha1 -sign privkey.pem -out sign.txt datafile 
Будет сформирована подпись sign.txt  для данных из файла datafilе. 
Сначала будет вычислен SHA-1 хэш по данным из datafile и затем сделана подпись.


GnuPG

gpg -u XXX --sign file

Создаст file.gpg, который будет содержать подпись пользователя XXX.

 

gpg -u XXX --clearsign file

Создаст file.asc, который будет содержать неизменённый текст файла file и подпись пользователяXXX.

 

Проверка подписи файла:

OpenSSL
openssl dgst -sha1 -verify pubkey.pem -signature sign.txt datafile
Проверит по открытому ключу pubkey.pem, что подпись данных из datafile,
которая находится в sign.txt, соответствует владельцу закрытого ключа. 

GnuPG

gpg --verify 1.txt.asc

Проверит и напечатает инфо о владельце ключа, которым подписано сообщение.

 

 

 

Теперь опишу процедуры для безопасной передачи автоматизированной данных между сервером и клиентом с помощью OpenSSL.

При этом сервер должен быть уверен, что это правильный клиент, а клиент должен быть уверен, что он попал на правильный сервер.

 

  • Сначала на сервере в каталоге /mycrt делается самоподписанный сертификат. Повторю ранее приведёную команду.

    openssl req -new -x509 -days 3660 -newkey rsa:2048 -keyout privkey.pem -out pubkey.crt \
    -subj "/C=RU/ST=Kamchatka/L=Elizovo/O=Roga I Kopyta/OU=IT/CN=www.mydomain.ru/emailAddress=my@addr.com"

     

  • Снимаю пароль с секретного ключа:
    mv privkey.pem privkey.pem.orig
    openssl rsa -in privkey.pem.orig -out privkey.pem
  • На сервере в каталоге (например /mycrt), где буду хранить ключи и сертификаты,
    создаю конфигурационный файл с именем ca.config:

    [ ca ]
         default_ca = CA_CLIENT       # При подписи сертификата
                                      # использовать секцию CA_CLIENT
    [ CA_CLIENT ]
        dir = /mycrt/db               # Каталог для служебных файлов
        certs = $dir/certs            # Каталог для сертификатов
        new_certs_dir = $dir/newcerts # Каталог для новых сертификатов
        database = $dir/index.txt     # Файл с базой данных
                                      # подписанных сертификатов
        serial = $dir/serial          # Файл содержащий серийный номер
                                      # сертификата
                                      # (в шестнадцатиричном формате)
        certificate = ./pubkey.crt    # Файл сертификата CA
        private_key = ./privkey.key   # Файл закрытого ключа CA
        default_days = 3650           # Срок действия в днях подписываемого
                                      # сертификата
        default_crl_days = 7          # Срок действия CRL
        default_md = md5              # Алгоритм подписи
        policy = policy_anything      # Название секции с описанием
                                      # политики в отношении данных
                                      # сертификата
    [ policy_anything ]
        countryName = optional        # RU
        stateOrProvinceName = optional
        localityName = optional      
        organizationName = optional  
        organizationalUnitName = optional
        commonName = supplied         # обязателен
        emailAddress = optional

    Делаю каталоги и файлы, соответствующие параметрам конфиге:
     mkdir db
    mkdir db/certs
    mkdir db/newcerts
    touch db/index.txt
    echo "01" > db/serial


    Примечание: В файле db/serial записывается текущий серийный номер подписываемого сертификата в шестнадцатиричном формате. В файл db/index.txt сохраняются данные о
    подписываемых сертификатах.
     
  • На клиентской машине клиент создаёт закрытый ключ и запрос на подпись сертификата (CSR):
    openssl req -new -days 3660 -newkey rsa:1024 -keyout privkey.pem -out pubkey.csr \
    -subj "/C=RU/ST=Kamchatka/L=Elizovo/O=Client Rogov i Kopyt/OU=IT/CN=www.cl.ru/emailAddress=cl@cl.com"


     
  • Снимается пароль с секретного клиентского ключа:
    mv privkey.pem privkey.pem.orig
    openssl rsa -in privkey.pem.orig -out privkey.pem


     
  • pubkey.crt копируется на сервер с именем pubkey01.crt и на сервере в каталоге /mycrt делаю:
    openssl ca -config ca.config -in pubkey01.csr -out pubkey01.crt -batch
     
  • В результате в этом каталоге будет создан pubkey01.crt, в базу данных он запишется с именем 01.pem и в файлах базы данных openssl проведёт необходимые изменения.
     
  • pubkey01.crt необходимо вернуть на клиента. Клиент по информации внутри подписанного сертификата может проверить, что ключ действительно пришёл от сервера. Также сервер теперь тоже имеет публичный ключ клиента и сможет проверить подпись от клиента.
     
  • Сервер должен в общий доступ выложить свой публичный ключ(точнее сертификат), а клиент скачать его. Теперь эта пара компьютеров обладает публичными ключами друг друга
    и могут шифровать, подписывать, расшифровывать и проверять подписи сообщений между собой.

 

РАЗНОЕ

Формат файла .PFX (ещё есть название PKCS12 для этого формата) используется для хранения закрытого ключа, подписанного сертификата и сертификата того сервера, который подписывал наш ключ (CA). Изготавливается сервером, на который мы собираемся коннектиться броузером и позволяет серверу удостовериться, что мы именно те, за кого себя выдаём. Созданный сервером этот наборчик клиент скачивает в виде PFX-файла через Интернет в надежде, что никто не перехватит этот "ключ от сейфа, где деньги лежат". Также PFX обычно снабжается паролем. Без пароля хакер ничего не сможет сделать с этим набором ключей.

 

WebMoney использует такой способ аутентификации клиента, т.е. вместо логина и пароля вы импортируете PFX-файл в броузер и при подключении к серверу броузер автоматом использует шифрование, понятное серверу и ему самому. Иногда надо превратить PFX в набор PEM-ов. Делается это так:

 

  • Сначала вытаскиваем все ключи в формате PEM в один файл. Пароль при конвертации отменяю.
    openssl pkcs12 -nodes -in file.pfx -out keys.pem
     
  • Выделяю из этого файла приватный ключ в формате PEM:
    openssl rsa -in keys.pem -out private.key
     
  • Выделяю редактором из keys.pem свой подписанный сертификат в файл public.crt. Определите свой сертификат по названию (CN).
     
  • Оставляю редактором в keys.pem только сертификаты сервера (стираю приватный ключ и свой сертификат). Называю файлcerts.ca
     

Теперь этот набор можно использовать, например в программе curl:

curl --key private.key --cert public.crt --cacerts certs.ca "https://mydomain.ru/verysecret.html"