Показаны сообщения с ярлыком openssl. Показать все сообщения
Показаны сообщения с ярлыком openssl. Показать все сообщения

воскресенье, 7 марта 2021 г.

Ubuntu: как настроить OpenVPN

С недавних пор заинтересовал меня вопрос, как можно попадать в свою домашнюю сеть из большого Интернета. Нет, конечно, проброшенные ssh и rdp-порты выручают, но всё-таки это как-то неизящно. Есть, правда, ещё pptp, он он, говорят, небезопасный... В общем, захотелось что-нибудь посолиднее, и выбор пал на OpenVPN.

Сама установка трудностей не представляет:

sudo apt install openvpn
Основная интрига - как этот самый openvpn-сервер сконфигурировать. В сети полно инструкций, как это сделать с использованием easy-rsa, но на поверку оказалось, что этот самый easy-rsa - просто набор bash-скриптов для работы с openssl, так что особого смысла в использовании этой дополнительной прослойки я не увидел и насоздавал ключи и сертификаты прямо с использованием openssl.

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

Удостоверяющий центр:
-- приватный ключ УЦ (бережно хранится и никому не показывается)
-- сертификат УЦ (раздаётся всем кому попало)
эта пара используется для подписи сертификатов сервера и клиента

Сервер OpenVPN:
-- сертификат УЦ
-- приватный ключ сервера (бережно хранится и никому не показывается)
-- сертификат сервера, подписанный УЦ (передаётся клиентам при установлении подключения)

Клиент, подключающийся к серверу:
-- сертификат УЦ
-- приватный ключ клиента (бережно хранится и никому не показывается)
-- сертификат клиента, подписанный УЦ (передаётся серверу при установлении подключения)
При подключении клиент и сервер обмениваются своими сертификатами. Сервер проверяет, что сертификат клиента подписан тем же УЦ, что и его собственный, а клиент делает такую же проверку с сертификатом сервера. Если оба сертификата прошли проверку, соединение устанавливается, сервер вытаскивает из сертификата клиента ComnmonName и использует его для идентификации клиента.

Создание удостоверяющего центра (УЦ)

Можно, конечно, использовать сторонние УЦ, но там, как правило, за работу требуют денежку. Поэтому логично поднять свой УЦ. Фактически, весь УЦ - это пара файлов, ключ и самоподписанный сертификат. Единственная тонкость - из соображений безопасности эту пару файлов желательно запрятать поглубже: вынести в docker, на отдельную машину, отправить на Луну... Так или иначе, создаются эти файлы следующим образом.

1. Создаём приватный ключ УЦ:

openssl genrsa    -des3   -out huhmuh-CA.key    2048

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

2. Создаём самоподписанный сертификат УЦ:

openssl req \
    -x509 \
    -days 3650 \
    -key huhmuh-CA.key \
    -out huhmuh-CA.crt \
    -subj "/emailAddress=huhmuh@example.com/C=RU/ST=Oblastnaya oblast/L=Default City/O=Home/OU=Living room/CN=example.com" \
    -addext "subjectAltName = DNS:example.com"

Можно "-subj" и не указывать, тогда при создании сертификата потребуется ответить на кучку вопросов, кто вы и откуда. Ну, и, поскольку используется huhmuh-CA.key, программа спросит для него пароль.

В результате этих манипуляций у нас на руках окажутся два файла, huhmuh-CA.key и huhmuh-CA.crt - это и будет наш удостоверяющий центр.

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

1. Создаём приватный ключ сервера:

openssl genrsa    -out huhmuh-openvpn.key    2048

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

2. Создаём запрос сертификата для сервера:

openssl req \
    -new \
    -key huhmuh-openvpn.key \
    -out huhmuh-openvpn.csr \
    -subj "/emailAddress=huhmuh@example.com/C=RU/ST=Oblastnaya oblast/L=Default City/O=Home/OU=Living room/CN=server"

Тут стоит обратить внимание на поле /CN=server. Его значение отличается от соответствующего поля, использовавшегося при создании сертификата УЦ. Если эти поля совпадают, то клиенты почему-то считают сертификат сервера самоподписанным, насколько я понял. Поэтому лучше CommonName сертификатов УЦ и сервера сделать различными, во избежание недоразумений.

3. Подписываем сертификат в удостоверяющем центре:

Как выяснилось в процессе эксплуатации, некоторые клиенты openvpn, в частности, 2.3, требуют, чтобы сертификат содержал расширения x509v3, в которых было бы явно указано, для чего его можно использовать. Делается это при помощи следующих расширений: keyUsage и extendedKeyUsage. Можно было бы их указывать на этапе создания запроса сертификата, но почему-то на этапе подписания они теряются. Но есть положительный момент: при подписании можно добавить нужные расширения. И момент отрицательный: соответствующий параметр командной строки "-addext" срабатывает только в openssl начиная с версии 1.1.1а.

Поэтому создадим на УЦ два файла с расширениями для серверных и клиентских сертификатов.

Файл server-extensions.txt:

[default]
keyUsage = digitalSignature,keyAgreement
extendedKeyUsage = serverAuth

Файл client-extensions.txt:

[default]
keyUsage = digitalSignature,keyAgreement
extendedKeyUsage = clientAuth

И будем их использовать при подписании.

Сам процесс подписания прост: передаём на Луну файл *.csr, подписываем его в УЦ командой:

openssl x509 \
    -req \
    -days 3650 \
    -in    huhmuh-openvpn.csr \
    -out   huhmuh-openvpn.crt \
    -CA    huhmuh-CA.crt \
    -CAkey huhmuh-CA.key \
    -CAcreateserial \
    -extfile server-extensions.txt

И возвращаем готовый сертификат huhmuh-openvpn.crt с Луны обратно на сервер, где будет жить OpenVPN.

4. Дальше потребуется сгенерировать ещё два файла, они нужны при настройке сервера. Эти файлы создаются так:

Создаём файл с параметрами для протокола Диффи-Хеллмана:

openssl dhparam    -out dh.pem     2048

Создаём приватный ключ для tls-аутентификации:

openvpn --genkey --secret ta.key

Тут интересно, что используется не команда openssl, а сама программа openvpn. У этой самой openvpn оказалось вообще много всяких параметров командной строки, посмотреть их можно командой openvpn --help. Программа живёт в каталоге /sbin или /usr/sbin, эти каталоги в $PATH простых смертных не прописаны, так что если вызывать её из-под непривилегированного пользователя, возможно потребуется указать полный путь: /sbin/openvpn.

Итак, по завершении этого этапа, в нашем распоряжении будут файлы:

huhmuh-CA.crt
huhmuh-openvpn.key
huhmuh-openvpn.crt
dh.pem
ta.key

Эти файлы потребуется сгрузить в каталог /etc/openvpn, туда же скопировать из /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz образец конфигурации, распаковать его командой

gunzip server.conf.gz

При этом получится файл server.conf, и этот файл уже можно править под свои нужды.

Файл server.conf богат на комментарии, между ними запрятаны настройки, которые должны быть примерно такими:

# тут можно указать, какой адрес использовать,
# но, поскольку мой сервер находится "в глубине" сети,
# то у него один адрес, поэтому я оставил эту настройку закомментированной
;local a.b.c.d

# порт по умолчанию 1194, но можно и сменить
port 32194

# протокол рекомендуют udp, но пусть уж так
proto tcp

dev tun

# наши ключи и сертификаты
ca       huhmuh-CA.crt
cert     huhmuh-openvpn.crt
key      huhmuh-openvpn.key  # This file should be kept secret
dh       dh.pem
tls-auth ta.key     0

topology subnet

# задаём, какой диапазон адресов будет использоваться клиентскими подключениями
# первый адрес, 10.8.0.1, сервер openvpn заберёт себе
server 10.8.0.0 255.255.255.0

# логи и прочая полезная информация.
# в частности, в ipp.txt показываются сопоставления CN клиентских сертификатов и выделенные для них адреса
ifconfig-pool-persist /var/log/openvpn/ipp.txt
status                /var/log/openvpn/openvpn-status.log
log-append            /var/log/openvpn/openvpn.log

# насколько подробным будет лог
verb 6

# с этой опцией клиент будет вынужден весь свой трафик завернуть через openvpn
push "redirect-gateway def1 bypass-dhcp"

keepalive 10 120

# версия 2.3 знает лишь AES-256-CBC, с версии 2.4 лучше применять AES-256-GCM, он надёжнее, говорят
;cipher AES-256-CBC
cipher AES-256-GCM

comp-lzo

# чтобы сервер не работал под рутом - для пущей безопасности
user nobody
group nogroup

persist-key
persist-tun

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

systemctl start openvpn@server

Вот это дополнение, @server, задаёт имя файла конфигурации, который будет использован, т.е., server.conf. Если же взлететь с первого раза не получилось, может выручить запуск сервера командой:

openvpn --config /etc/openvpn/server.conf

И медитация над логами из /var/log/openvpn/ в попытке разобраться, что же пошло не так...

Создание файла подключения клиента

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

1. Создаём приватный ключ клиента:

openssl genrsa    -des3    -out huhmuh-alice.key    2048

Ключ шифруем (опция "-des3") - пусть клиент при установлении подключения вводит пароль.

2. Создаём запрос сертификата для клиента:

openssl req \
    -new \
    -key huhmuh-alice.key \
    -out huhmuh-alice.csr \
    -subj "/emailAddress=alice@example.com/C=RU/ST=Oblastnaya oblast/L=Default City/O=Home/OU=Kitchen/CN=alice"

3. Подписываем сертификат клиента в удостоверяющем центре:

openssl x509 \
    -req \
    -days 3650 \
    -in    huhmuh-alice.csr \
    -out   huhmuh-alice.crt \
    -CA    huhmuh-CA.crt \
    -CAkey huhmuh-CA.key \
    -CAcreateserial \
    -extfile client-extensions.txt

В итоге для клиентского подключения мы приготовили файлы:

huhmuh-CA.crt
huhmuh-alice.key
huhmuh-alice.crt

Из этих файлов и файла /etc/openvpn/ta.key можно собрать файл подключения. Проще всего это сделать примерно таким скриптом:

#!/bin/sh

FNAME=huhmuh-alice.ovpn

echo "client
dev tun
proto tcp
remote example.com 32194
resolv-retry infinite
remote-cert-tls server
auth SHA1
cipher AES-256-GCM
tls-client
key-direction 1
persist-key
persist-tun
resolv-retry infinite
nobind
comp-lzo
verb 3"				>  $FNAME

echo "<ca>"			>> $FNAME
cat ./huhmuh-CA.crt		>> $FNAME
echo "</ca>"			>> $FNAME

echo "<cert>"			>> $FNAME
cat ./huhmuh-alice.crt		>> $FNAME
echo "</cert>"			>> $FNAME

echo "<key>"			>> $FNAME
cat ./huhmuh-alice.key		>> $FNAME
echo "</key>"			>> $FNAME

echo "<tls-auth>"		>> $FNAME
cat /etc/openvpn/ta.key		>> $FNAME
echo "</tls-auth>"		>> $FNAME

Тут надо помнить пару моментов. Во-первых, /etc/openvpn/ta.key - секретный (хотя как секретный, клиенту же передаётся?), поэтому доступен лишь под рутом. И, во-вторых, соответствующие настройки должны совпадать с настройками сервера. Например, если в конфиге сервера стоит "cipher AES-256-CBC", то и у клиента должно быть то же самое, "cipher AES-256-CBC". Про "remote example.com 32194", думаю, упоминать смысла нет - и так понятно, что это адрес сервера и открытый на нем порт.

Если всё прошло нормально, то в нашем распоряжении оказывается файл huhmuh-alice.ovpn, который можно отдавать клиенту, чтобы он с ним подключался к нашему серверу. Клиентов для OpenVPN хватает, даже под андроидом есть некий OpenVPN Connect, так что тип операционной системы использование этого инструмента не лимитирует.

(NB: для WindowsXP, как оказалось, последняя версия клиента: 2.3.18. При её настройке из скопированного в C:\Program Files\OpenVPN\config файла .ovpn пришлось удалить строку set CLIENT_CERT 0. Также эта версия начала кочевряжиться при отсутствии Key Usage в расширениях серверного сертификата, о чем было упомянуто выше. Помогает либо добавление требуемого расширения, либо убирание строки remote-cert-tls server в файле *.ovpn)

Настройка netfilter на сервере

В случае успеха клиенты, подключенные к серверу, смогут добраться лишь до этого самого сервера. Попытка попасть на какие-то другие хосты в локальной сети окажется неудачной. Для того, чтобы клиенты получили возможность пройти дальше сервера, требуется использовать nat. В принципе, можно сконфигурировать openvpn так, чтобы он позволил своим клиентам получать адреса из диапазона локальной сети. Но, поскольку в вышеприведенной конфигурации клиенты получают адреса из диапазона 10.8.0.0/24, а остальная сеть живёт в диапазоне 192.168.0.0/24, то в данном случае придётся воспользоваться iptables. Минимальная конфигурация, позволяющая это осуществить, выглядит так:

#!/bin/sh

# подключаем nf_conntrack и разрешаем проброс пакетов
modprobe nf_conntrack
sysctl net.ipv4.ip_forward=1


# очищаем таблицы nat и filter
iptables -F -t nat
iptables -F -t filter

iptables -X -t nat
iptables -X -t filter


# разрешаем работать исходящим и уже установленным подключениям
iptables -t filter -A INPUT   -m conntrack --ctstate     ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -A FORWARD -m conntrack --ctstate     ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -A OUTPUT  -m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT


# разрешаем доступ к серверу по перечисленным портам (не один же openvpn у нас тут крутится)
iptables -t filter -A INPUT -p tcp -m multiport --dports 22,67,68,80,443,32194 -j ACCEPT
iptables -t filter -A INPUT -p udp -m multiport --dports 67,68                 -j ACCEPT
# 22 -ssh, 67 - dhcp, 80,443 - https, 32194 - openvpn


# в трафик из подсети 10.8.0.0/24 подставляем адрес сервера в локальной сети, 192.168.0.2, в качестве источника
iptables -t filter -A FORWARD     -s 10.8.0.0/24  -j ACCEPT
iptables -t nat    -A POSTROUTING -s 10.8.0.0/24  -j SNAT    --to-source 192.168.0.2


# остальные пакеты, не прошедшие правила, отбрасываем (кроме исходящих)
iptables -t filter -P INPUT   DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -P OUTPUT  ACCEPT

Настройка клиента на ubuntu

Подключение к серверу с использованием имеющегося файла huhmuh-alice.ovpn выглядит так:

sudo openvpn --config ./huhmuh-alice.ovpn

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

Оказывается, в openvpn есть две внутренние константы, vpn_gateway и net_gateway. Они содержат адрес vpn-шлюза и шлюза локальной сети соответственно, и с их помощью можно задавать нужные маршруты. В файл *.ovpn добавляем строку:

route 192.168.0.0 255.255.255.0 vpn_gateway

Клиента запускаем командой:

sudo openvpn --pull-filter ignore redirect-gateway --config ./huhmuh-alice.ovpn

И весь трафик будет ходить по-старому, за исключением сети 192.168.0.0/24 - общение с ней пойдёт через vpn.

Литература

Установка и настройка OpenVPN-сервера в Debian
Конспект по установке OpenVPN
Установка и настройка сервера OpenVPN в Ubuntu 20.04
OpenSSL Quick Reference Guide
Create SSL certificate non-interactively
Генерирование сертификатов для OpenVPN с помощью Easy-RSA 3
x509v3_config
OpenVPN Certificate does not have key usage extension
OpenSSL CA keyUsage extension
IX509ExtensionEnhancedKeyUsage interface (certenroll.h)
Class ExtendedKeyUsage
Connecting to Access Server with Linux
Ignoring redirect-gateway

среда, 20 мая 2020 г.

Debian: python-скрипт, sql server 2005 и настройки openssl

Оказывается, если при переезде на новое место жительства скрипт, написанный в незапамятные времена на питоне и общающийся с Sql Server 2005 через pyodbc+unixODBC+msodbcsql (вот такое вот извращение), стал жаловаться примерно так: [ODBC Driver 17 for SQL Server]SSL Provider: [error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol], то можно поправить настройки ssl в файле /etc/ssl/openssl.cnf:
[system_default_sect]
MinProtocol = TLSv1.0
CipherString = DEFAULT@SECLEVEL=1

понедельник, 26 октября 2015 г.

Linux: PostgreSQL 9.x и сертификаты

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

Как я понял, PostgreSQL использует сертификаты в двух случаях: для установления защищенного соединения и для проверки подлинности пользователя. В любом случае используется информация:

1) адрес или имя сервера баз данных, по которому будет стучаться клиент, например, myserver

2) имя системного пользователя, который пытается авторизоваться, например, sysUser

3) имя пользователя базы данных, под которым пытается авторизоваться системный пользователь, например, pgUser

При этом затрагиваются следующие каталоги и файлы конфигурации:

1) На стороне сервера: файлы postgresql.conf, pg_hba.conf и pg_ident.conf. Эти файлы в зависимости от сборки могут располагаться в разных местах. Например, на дебиане они обнаружились в каталоге /etc/postgresql, а на слакваре при установке из исходных кодов обосновались в рабочем каталоге /usr/local/pgsql/data

2) На стороне клиента: каталог ~/.postgresql/, в который должна быть положена пара ключ-сертификат, использующаяся для проверки подлинности пользователя.

Итак, настраиваем все эти сертификаты. Так как я - по натуре нищеброд, то сертификаты у меня будут самоподписанные, а создавать их буду бесплатной утилитой openssl.

Сначала генерируем корневой сертификат, которым будем подписывать сертификаты сервера и клиента:
# генерируем приватный ключ
openssl genrsa -out root.key 1024

# создаем самоподписанный корневой сертификат
openssl req -new -x509 -days 1826 -key root.key -out root.crt
При этом будут заданы всякие вопросы, от ответов, я так понял, мало что зависит.

Затем при помощи этого корневого сертификата создаем сертификат сервера:
# генерируем приватный ключ
openssl genrsa -out server.key 1024

# создаем запрос сертификата
# тут надо внимательно отнестись к заполнению полей:
# в поле Common Name (CN) надо указать адрес или имя сервера баз данных
# (например, myserver)
openssl req -new -utf8 -key server.key -out server.csr

# из запроса создаём сертификат сервера, подписанный корневым сертификатом
openssl x509 -req -days 730 -in server.csr -CA root.crt -CAkey root.key -out server.crt

У нас получилось три ценных файла: root.crt, server.key, server.crt. Их нужно запихнуть в рабочий каталог postgresql (тот самый что-то-там/data, в котором при установке СУБД делалась команда initdb). При этом на файл server.key налагаются суровые ограничения по безопасности: он должен иметь владельцем пользователя, под которым работает СУБД, и уровень доступа -rw-------.

Теперь сгенерируем сертификат для клиента:
# генерируем приватный ключ
openssl genrsa -out postgresql.key 1024

# создаем запрос сертификата
# тут тоже надо внимательно отнестись к заполнению полей:
# в поле Common Name (CN) следует указать имя системного пользователя,
# для которого создаётся сертификат
# (например, sysUser)
openssl req -new -utf8 -key postgresql.key -out postgresql.csr

# из запроса создаём сертификат клиента, подписанный корневым сертификатом
openssl x509 -req -days 730 -in postgresql.csr -CA root.crt -CAkey root.key -out postgresql.crt -CAcreateserial

После этой процедуры два файла, postgresql.key и postgresql.crt, закидываем клиенту в папку ~/.postgresql/ и у файла postgresql.key опять-таки выставляем правильного владельца и права -rw-------.

Стоит отметить, что содержимое получившихся сертификатов, в частности, заполнение поля CN можно всегда посмотреть командой:
openssl x509 -in postgresql.crt -text -noout -nameopt oneline,-esc_msb,utf8

Так или иначе, осталось написать правильные настройки сервера баз данных:

1) в файле postgresql.conf проверяем, что включена опция ssl = on

2) прописываем в pg_hba.conf строку:
#TYPE    DATABASE  USER    ADDRESS   METHOD
hostssl  all       pgUser  myserver  cert clientcert=1 map=myMap
Тут мы указали, что соединение должно быть защищено ssl, для авторизации следует использовать сертификат клиента и при сопоставлении системного имени и имени пользователя баз данных будет использоваться запись с меткой myMap из файла pg_ident.conf

3) в файле pg_ident.conf прописываем строку:
#MAPNAME  SYSTEM-USERNAME  PG-USERNAME
myMap     sysUser          pgUser
На самом деле, как я понял, этот файл содержит довольно простую информацию: какому системному пользователю под каким именем СУБД разрешается подключаться к серверу. То есть, если пользователю позволено подключаться под разными именами, pg_ident.conf будет выглядеть так:
#MAPNAME  SYSTEM-USERNAME  PG-USERNAME
myMap     sysUser          pgUser1
myMap     sysUser          pgUser2

4) перезапускаем сервер PostgreSQL.

Процесс авторизации по этой схеме выглядит, насколько я понял, так:

1) Устанавливается защищенное соединение при помощи сертификатов server.key + server.crt

2) От клиента сервер получает сертификат postgresql.crt, из которого выясняет системное имя пользователя. Имя пользователя базы данных, под которым хочет зайти системный пользователь, указывается явно в строке подключения.

3) По имени пользователя базы данных, имени сервера и режиму соединения (hostssl) определяется строка в файле pg_hba.conf, из которой выясняется имя метки в файле pg_ident.conf.

4) По метке в файле pg_ident.conf устанавливается, может ли данный системный пользователь авторизоваться в СУБД под данным пользователем баз данных.

Вообще говоря, требование размещения клиентских сертификатов в каталоге ~/.postgresql/ не является таким уж обязательным. Просто их там по умолчанию ищет библиотека libpq. В произвольных же скриптах можно указывать явно, какие файлы использовать в качестве пары ключ-сертификат. Вот, например, скрипт на Python-е:
import psycopg2

connString = "host=myserver"             + \
             " user=pgUser"              + \
             " dbname=postgres"          + \
             " sslcert=./postgresql.crt" + \
             " sslkey=./postgresql.key"

conn = psycopg2.connect(connString)

вторник, 15 сентября 2015 г.

eToken: создать сертификат при помощи openssl

Дано:

1) USB-ключ eToken PRO (JAVA).
2) Операционная система Windows XP с установленным в ней eToken PKI Client-ом.
3) Операционная система Slackware 14.1
4) Центр сертификации Microsoft Active Directory Certificate Services со своим веб-интерфейсом.
5) Некий веб-ресурс, который умеет общаться по https с использованием сертификатов, выданных центром сертификации из п.4.

Требуется:

1) Получить сертификат в центре сертификации
2) Записать полученный сертификат на eToken
3) Подключиться к требуемому веб-ресурсу из-под Windows XP с использованием браузера Internet Explorer.

Решение:

Вообще-то, можно было бы воспользоваться веб-интерфейсом центра сертификации. Но есть маленькая проблема: этот центр с некоторых пор перестал воспринимать Windows XP.

Выглядит это так. Если попытаться при помощи IE8 сделать запрос сертификата через задачу "Advanced certificate request", центр сертификации присылает HTTP-ответ, в котором Content-length указано примеро 90кб, а реальная длина ~30кб, причем невооруженным глазом видно, что ответ оборван посередине какого-то vb-скрипта. Не знаю, с чем это связано, тем более, что если подменять User-agent браузера на другие варианты (например, при помощи чудесной утилиты UAPick), то HTTP-ответы формируются нормально.

Можно было бы при помощи подмены User-agent бы притвориться, скажем, IE9+Win7, но этот вариант не подходит: в современных системах работа с сертификатами на стороне клиента реализуется при помощи CertEnroll API, а в Windows XP для этой цели использовалось XEnroll API. Соответственно, если центр сертификации считает клиента работающим под новой ОС, он отправляет ему скрипты, которые выполняются с ошибками.

Остается один вариант: создать запрос на сертификат при помощи чего-нибудь стороннего, а потом, скажем, через FireFox его отправить в центр сертификации, благо, тот для "неподходящих" браузеров предлагает соответствующий интерфейс.

К счастью, под рукой оказался линукс с его openssl. Схема работы такова:

1. Генерируем 1024-битный ключ (eToken на ключи с большей длиной у меня почему-то ругается):
openssl genrsa -out 0000.key 1024

2. При помощи полученного ключа 0000.key делаем запрос сертификата:
openssl req -new -utf8 -config my.conf -key 0000.key -out 0000.csr
Тут обратим внимание на две опции: -utf8 позволяет заполнять поля запроса русскими буквами, а -config my.conf позволяет в файле my.conf выполнить "тонкую настройку", в частности, у меня этот файл выглядит так:
[req]
default_bits           = 1024
distinguished_name     = req_distinguished_name
req_extensions         = req_ext

[req_distinguished_name]
countryName                    = Country Name (2 letter code)
countryName_default            = RU
countryName_min                = 2
countryName_max                = 2

organizationalUnitName         = Organizational Unit Name (eg, section)

commonName                     = Common Name (eg, YOUR name)
commonName_max                 = 64

[req_ext]
extendedKeyUsage = clientAuth

3. Далее открываем получившийся файл 0000.csr в блокноте и копируем его содержимое в соответствующее поле ввода на веб-странице центра сертификации. Этот центр его переваривает и выдаёт сертификат, скажем, 0000.cer

4. Ключ 0000.key и сертификат 0000.cer сливаем в один файл 0000.p12:
openssl pkcs12 -export -out 0000.p12 -inkey 0000.key -in 0000.cer
и закидываем этот файл на eToken - там в PKI-клиенте есть команда "Импорт сертификата".

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

Литература:
http://www.websense.com/support/article/kbarticle/How-to-use-OpenSSL-and-Microsoft-Certification-Authority
https://www.openssl.org/docs/manmaster/apps/x509v3_config.html