CryptodevATECCx08 Auth: различия между версиями

Материал из Wiren Board
Метка: visualeditor
(не показано 9 промежуточных версий 4 участников)
Строка 1: Строка 1:
<languages/>
<translate>
== Использование встроенного чипа ATECCx08 для авторизации на внешних сервисах. == <!--T:1-->


== Использование встроенного чипа ATECCx08 для авторизации на внешних сервисах. ==
<!--T:2-->
 
В контроллеры '''WirenBoard''' встроен чип '''ATECCx08''', назначением которого является генерация ключевых пар, хранение приватных ключей а также операции асимметричного шифрования с использованием эллиптических кривых.
В контроллеры '''WirenBoard''' встроен чип '''ATECCx08''', назначением которого является генерация ключевых пар, хранение приватных ключей а также операции асимметричного шифрования с использованием эллиптических кривых.
Приватный ключ после начальной инициализации хранится в микросхеме и не покидает ее, тем самым исключается его компрометация.
Приватный ключ после начальной инициализации хранится в микросхеме и не покидает ее, тем самым исключается его компрометация.


<!--T:3-->
Используя данную микросхему можно организовать авторизацию контроллера и защиту соединения по SSL на внешних сервисах и быть уверенным, что запрос выполнен с использованием именно этого экземпляра микросхемы.
Используя данную микросхему можно организовать авторизацию контроллера и защиту соединения по SSL на внешних сервисах и быть уверенным, что запрос выполнен с использованием именно этого экземпляра микросхемы.


<!--T:4-->
В данной статье пойдет речь о трех вариантах применения: '''nginx''', '''openssl''', '''mosquitto'''.
В данной статье пойдет речь о трех вариантах применения: '''nginx''', '''openssl''', '''mosquitto'''.


<!--T:5-->
Для доступа к криптоустройству нужна библиотека libateccssl1.1, установим ее командой:
Для доступа к криптоустройству нужна библиотека libateccssl1.1, установим ее командой:


<!--T:6-->
'''apt install libateccssl1.1'''
'''apt install libateccssl1.1'''


<!--T:7-->
Далее нужно отредактировать файл '''/etc/ssl/openssl.cnf''', добавив в нем следующие строчки:
Далее нужно отредактировать файл '''/etc/ssl/openssl.cnf''', добавив в нем следующие строчки:


<!--T:8-->
<pre>
<pre>
openssl_conf = openssl_init
openssl_conf = openssl_init


<!--T:9-->
[openssl_init]
[openssl_init]
engines        = engine_section
engines        = engine_section


<!--T:10-->
[engine_section]
[engine_section]
ateccx08 = ateccx08_section
ateccx08 = ateccx08_section


<!--T:11-->
[ateccx08_section]
[ateccx08_section]
init = 1
init = 1
</pre>
</pre>


<!--T:12-->
Теперь приступим к созданию сертификатов.
Теперь приступим к созданию сертификатов.
Для начала создадим свой центр сертификации (Certification Authority, '''CA'''):
Для начала создадим свой центр сертификации (Certification Authority, '''CA'''):


<!--T:13-->
Для этого сгенерируем ключевую пару:
Для этого сгенерируем ключевую пару:
<pre>openssl genrsa -out ca.key 2048</pre>
<pre>openssl genrsa -out ca.key 2048</pre>


<!--T:14-->
И сертификат нашего '''CA''':
И сертификат нашего '''CA''':


<!--T:15-->
<pre>openssl req -x509 -new -days 3650 -key ca.key -out ca.crt -subj "/CN=MY CA"</pre>
<pre>openssl req -x509 -new -days 3650 -key ca.key -out ca.crt -subj "/CN=MY CA"</pre>


<!--T:16-->
'''CA''' является основой безопасности в данной схеме, поэтому эти операции выполняем на машине доступ к которой
'''CA''' является основой безопасности в данной схеме, поэтому эти операции выполняем на машине доступ к которой
есть только у владельца '''CA'''.
есть только у владельца '''CA'''.


<!--T:17-->
Далее на конроллере WB создаем запрос на сертификат устройства:
Далее на конроллере WB создаем запрос на сертификат устройства:
<!--T:18-->
Для Wiren Board 7:
<pre>openssl req -new -engine ateccx08 -keyform engine -key ATECCx08:00:02:C0:00 -subj "/CN=wirenboard-AP6V5MDG" -out device_AP6V5MDG.csr</pre>
Для Wiren Board 6:


<pre>openssl req -new -engine ateccx08 -keyform engine -key ATECCx08:00:04:C0:00 -subj "/CN=wirenboard-AP6V5MDG" -out device_AP6V5MDG.csr</pre>
<pre>openssl req -new -engine ateccx08 -keyform engine -key ATECCx08:00:04:C0:00 -subj "/CN=wirenboard-AP6V5MDG" -out device_AP6V5MDG.csr</pre>


<!--T:19-->
В этой команде мы указываем, что запрос подписывается приватным ключом, находящимся в криптоустройстве ateccx08 ключом
В этой команде мы указываем, что запрос подписывается приватным ключом, находящимся в криптоустройстве ateccx08 ключом
с идентификатором '''ATECCx08:00:04:C0:00''' и публичным именем wirenboard-AP6V5MDG. Запрос помещаем в файл device_AP6V5MDG.csr.
с идентификатором '''ATECCx08:00:02:C0:00''' и публичным именем wirenboard-AP6V5MDG. Запрос помещаем в файл device_AP6V5MDG.csr.


<!--T:20-->
Имя можно выбрать любое уникальное, удобно для этой цели спользовать идентификатор устройства, который по умолчанию прописывается
Имя можно выбрать любое уникальное, удобно для этой цели спользовать идентификатор устройства, который по умолчанию прописывается
в файле /etc/hostname:
в файле /etc/hostname:


<!--T:21-->
<pre>
<pre>
cat /etc/hostname
cat /etc/hostname
Строка 56: Строка 84:
</pre>
</pre>


<!--T:22-->
Далее этот запрос подписываем в нашем центре сертификации:
Далее этот запрос подписываем в нашем центре сертификации:
openssl x509 -req -in device_AP6V5MDG.csr -CA ca.crt -CAkey ca.key -out device_AP6V5MDG.crt -days 365 -CAcreateserial
openssl x509 -req -in device_AP6V5MDG.csr -CA ca.crt -CAkey ca.key -out device_AP6V5MDG.crt -days 365 -CAcreateserial


<!--T:23-->
В этой команде мы уазываем файл запроса, и файлы CA необходимые для подписи. В итоге получаем сертификат устройства device_AP6V5MDG.crt.
В этой команде мы уазываем файл запроса, и файлы CA необходимые для подписи. В итоге получаем сертификат устройства device_AP6V5MDG.crt.
Файл '''device_AP6V5MDG.crt''' копируем на контроллер WB, он будет необходим для авторизацци.
Файл '''device_AP6V5MDG.crt''' копируем на контроллер WB, он будет необходим для авторизацци.


<!--T:24-->
Давайте посмотрим, что содержится в файлах сертификатов CA и устройства:
Давайте посмотрим, что содержится в файлах сертификатов CA и устройства:


<!--T:25-->
<pre>
<pre>
openssl x509 -text -noout -in device_AP6V5MDG.crt
openssl x509 -text -noout -in device_AP6V5MDG.crt


<!--T:26-->
Certificate:
Certificate:
     Data:
     Data:
Строка 107: Строка 140:
</pre>
</pre>


<!--T:27-->
Видим, что сертификат выписан Центром сертификации с именем "MY CA" на 1 год, начная с "Feb  4 14:50:14 2019 GMT" (для этого мы указывали -days 365 в команде подписи),
Видим, что сертификат выписан Центром сертификации с именем "MY CA" на 1 год, начная с "Feb  4 14:50:14 2019 GMT" (для этого мы указывали -days 365 в команде подписи),
устройству с именем '''wirenboard-AP6V5MDG''', имеющим приватный ключ соответствующий публичному Subject Public Key Info.
устройству с именем '''wirenboard-AP6V5MDG''', имеющим приватный ключ соответствующий публичному Subject Public Key Info.
Сертификат подписан цифровой подписью.  
Сертификат подписан цифровой подписью.  


<!--T:28-->
Цифровая подпись гарантирует, что никакая часть сертификата не может быть незаметно изменена. В случае изменения проверка публичным
Цифровая подпись гарантирует, что никакая часть сертификата не может быть незаметно изменена. В случае изменения проверка публичным
ключом '''CA''' даст ошибку.
ключом '''CA''' даст ошибку.


<!--T:29-->
<pre>
<pre>
openssl x509 -text -noout -in ca.crt
openssl x509 -text -noout -in ca.crt


<!--T:30-->
Certificate:
Certificate:
     Data:
     Data:
Строка 157: Строка 194:
                 keyid:53:4B:6D:7A:1A:1F:F8:BE:3F:59:64:70:2B:31:2F:7A:7F:2A:6B:14
                 keyid:53:4B:6D:7A:1A:1F:F8:BE:3F:59:64:70:2B:31:2F:7A:7F:2A:6B:14


             X509v3 Basic Constraints: critical
             <!--T:31-->
X509v3 Basic Constraints: critical
                 CA:TRUE
                 CA:TRUE
     Signature Algorithm: sha256WithRSAEncryption
     Signature Algorithm: sha256WithRSAEncryption
Строка 177: Строка 215:
</pre>
</pre>


<!--T:32-->
Видим, что сертификат подписан сам своим-же приватным ключом (Issuer: CN = MY CA, Subject: CN = MY CA)
Видим, что сертификат подписан сам своим-же приватным ключом (Issuer: CN = MY CA, Subject: CN = MY CA)
и выписан на 10 лет: начная с "Feb  4 14:30:01 2019 GMT" (для этого мы указывали -days 3650 в команде подписи)
и выписан на 10 лет: начная с "Feb  4 14:30:01 2019 GMT" (для этого мы указывали -days 3650 в команде подписи)


<!--T:33-->
Таким образом цепочка доверия (проверки) выстраивается следующим образом:
Таким образом цепочка доверия (проверки) выстраивается следующим образом:


<!--T:34-->
Сертификат устройства подписан сертификатом CA и может быть им проверен:
Сертификат устройства подписан сертификатом CA и может быть им проверен:
<pre>
<pre>
Строка 188: Строка 229:
</pre>
</pre>


<!--T:35-->
Ну а сам сертификат подписан "собой":
Ну а сам сертификат подписан "собой":
<pre>
<pre>
Строка 195: Строка 237:
'''
'''


== Проверка сертификатов на сервере с помощью nginx . == <!--T:36-->


== Настройка nginx. ==
<!--T:37-->
 
Допустим в интернете есть сервер, который должен обрабатывать запросы только от устройств обладающих сертификатами,
Допустим в интернете есть сервер, который должен обрабатывать запросы только от устройств обладающих сертификатами,
выписанными нашим CA.
выписанными нашим CA.
Для включения такой проверки в конфигурационном файле nginx необходимо в секции http или server прописать следующие строчки:
Для включения такой проверки в конфигурационном файле nginx необходимо в секции http или server прописать следующие строчки:


<!--T:38-->
<pre>
<pre>
   ssl_client_certificate  ca.crt;
   ssl_client_certificate  ca.crt;
Строка 207: Строка 250:
</pre>
</pre>


<!--T:39-->
Теперь nginx будет требовать от клиента сертификат, который должен проходить проверку с помощью ca.crt, иначе сервер
Теперь nginx будет требовать от клиента сертификат, который должен проходить проверку с помощью ca.crt, иначе сервер
вернет клиенту ошибку 400:
вернет клиенту ошибку 400:


<!--T:40-->
<pre>
<pre>
curl https://example.com
curl https://example.com
Строка 222: Строка 267:
</pre>
</pre>


Nginx в такой конфигурации удобно использовать на сервере перед реальным сервисом, которые не поддерживают проверку клиентских сертификатов. Для этого нужно настроить proxy_pass на сам сервис и, например, добавить в заголовки серийный номер контроллера через переменную <code>$ssl_client_s_dn</code> или весь клиентский сертификат целиком.
== Добавление поддержки клиентских сертификатов к произвольным сервисам на контроллере ==
Часто программы на контролелере не поддерживают TLS совсем, либо не поддерживают указание клиентских сертификатов, либо не поддерживают работу с openssl engine.
Можно отправить обращения от таких программ к серверу сквозь локальный прокси, который будет оборачивать трафик в TLS с использованием аппаратных ключей.
Вместе с рецептом из предыдущего пункта, это позволяет реализовать аутентификацию по аппаратному ключу практически для любого клиент-серверного ПО.
<!--T:41-->
Теперь сделаем так, чтобы HTTP запросы с контроллера WB проходили данную проверку.
Теперь сделаем так, чтобы HTTP запросы с контроллера WB проходили данную проверку.
Для простоты будем использовать nginx и на клиентской стороне. Это даст возможность работать
Для простоты будем использовать nginx и на клиентской стороне. Это даст возможность работать
с защищенными серверами клиентам не умеющим делать SSL соединения.
с защищенными серверами клиентам не умеющим делать SSL соединения.


<!--T:42-->
Для начала создадим на неиспользуемом локальном порту http сервер, который будет делать
Для начала создадим на неиспользуемом локальном порту http сервер, который будет делать
всю https "магию" за нас:
всю https "магию" за нас:


<!--T:43-->
<pre>
<pre>
server {
server {
Строка 242: Строка 299:
</pre>
</pre>


<!--T:44-->
Добавим пользователя www-data в группу i2c для доступа к криптоустройству:
Добавим пользователя www-data в группу i2c для доступа к криптоустройству:
<pre>
<pre>
Строка 247: Строка 305:
</pre>
</pre>


<!--T:45-->
Выполним команду '''service nginx restart''' для обновления конфигурации.
Выполним команду '''service nginx restart''' для обновления конфигурации.


<!--T:46-->
Теперь при обращении по http на локальный порт 8080 зашифрованные запросы с аутентификационной
Теперь при обращении по http на локальный порт 8080 зашифрованные запросы с аутентификационной
информацией будут отправлятся на сервер example.com.
информацией будут отправлятся на сервер example.com.


<!--T:47-->
<pre>
<pre>
curl localhost:8080/
curl localhost:8080/
Строка 261: Строка 322:
</pre>
</pre>


== Настройка openvpn ==
== Настройка openvpn == <!--T:48-->


<!--T:49-->
Для начала установим пакет:
Для начала установим пакет:


<!--T:50-->
<pre>
<pre>
apt install openvpn
apt install openvpn
</pre>
</pre>


<!--T:51-->
Cоздадим файл req.cnf - он нам потребуется для создания сертификата сервера.  
Cоздадим файл req.cnf - он нам потребуется для создания сертификата сервера.  
<pre>
<pre>
Строка 277: Строка 341:
</pre>
</pre>


<!--T:52-->
Серверу openvpn также требуется файл с параметрами DH, сделаем его.
Серверу openvpn также требуется файл с параметрами DH, сделаем его.
<pre>
<pre>
Строка 282: Строка 347:
</pre>
</pre>


<!--T:53-->
Создание данного файла может потребовать несколько минут.  
Создание данного файла может потребовать несколько минут.  


<!--T:54-->
Далее сделаем приватный ключ нашего сервера и запрос на сертификат. Предполагаем, для примера, что
Далее сделаем приватный ключ нашего сервера и запрос на сертификат. Предполагаем, для примера, что
сервер имеет имя example.com:
сервер имеет имя example.com:


<!--T:55-->
<pre>  
<pre>  
openssl genrsa -out example.key 2048
openssl genrsa -out example.key 2048


<!--T:56-->
openssl req -new -key example.key -subj "/CN=example.com" -out example.csr
openssl req -new -key example.key -subj "/CN=example.com" -out example.csr
</pre>
</pre>


<!--T:57-->
Подписываем запрос в нашем центре сертификации:
Подписываем запрос в нашем центре сертификации:


<!--T:58-->
<pre>
<pre>
openssl x509 -req -in example.csr -CA ca.crt -CAkey ca.key -out example.crt -days 365 -CAcreateserial -extfile req.cnf -extensions v3_req
openssl x509 -req -in example.csr -CA ca.crt -CAkey ca.key -out example.crt -days 365 -CAcreateserial -extfile req.cnf -extensions v3_req
</pre>
</pre>


<!--T:59-->
Теперь приступим к настройке openvpn сервера на машине example.com.
Теперь приступим к настройке openvpn сервера на машине example.com.


<!--T:60-->
Копируем файлы '''ca.crt''', '''example.crt''', '''example.key''' и '''dh2048.pem''' и редактируем файл конфигурации openvpn сервера.
Копируем файлы '''ca.crt''', '''example.crt''', '''example.key''' и '''dh2048.pem''' и редактируем файл конфигурации openvpn сервера.
По умолчанию конфигурационный файл лежит в файле /etc/openvpn/server.conf
По умолчанию конфигурационный файл лежит в файле /etc/openvpn/server.conf


<!--T:61-->
<pre>
<pre>
port 1194
port 1194
Строка 328: Строка 402:
</pre>
</pre>


<!--T:62-->
Добавим пользователя openvpn в группу i2c для доступа к криптоустройству:
Добавим пользователя openvpn в группу i2c для доступа к криптоустройству:


<!--T:63-->
<pre>
<pre>
usermod -G www-data
usermod -G www-data
</pre>
</pre>


<!--T:64-->
после этого запускаем сервер командой  
после этого запускаем сервер командой  
service openvpn start.
service openvpn start.


<!--T:65-->
Далее на контроллере создаем файл конфигурации клиента:
Далее на контроллере создаем файл конфигурации клиента:
client.ovpn:
client.ovpn:
<pre>
-------------------------------------------------------------------------
-------------------------------------------------------------------------
client
client
Строка 357: Строка 436:
comp-lzo
comp-lzo
-------------------------------------------------------------------------
-------------------------------------------------------------------------
 
</pre>
Обратите внимание на строчку group i2c. Она необходима для работы с криптоустройством.
Обратите внимание на строчку group i2c. Она необходима для работы с криптоустройством.


<!--T:66-->
После этого запускаем клиента:
После этого запускаем клиента:


<!--T:67-->
<pre>
<pre>
openvpn --config example.ovpn --ca ca.crt --cert device_AP6V5MDG.crt --key engine:ateccx08:ATECCx08:00:04:C0:00
openvpn --config example.ovpn --ca ca.crt --cert device_AP6V5MDG.crt --key engine:ateccx08:ATECCx08:00:04:C0:00
</pre>
</pre>


<!--T:68-->
Если все хорошо, то в системе должен появиться интерфейс tun0 с адресом из подсети 10.8.0.0/24:
Если все хорошо, то в системе должен появиться интерфейс tun0 с адресом из подсети 10.8.0.0/24:


<!--T:69-->
<pre>
<pre>
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
Строка 379: Строка 462:
</pre>
</pre>


<!--T:70-->
Для проверки работоспособности запускаем ping:
Для проверки работоспособности запускаем ping:


<!--T:71-->
<pre>
<pre>
ping 10.8.0.1
ping 10.8.0.1
Строка 388: Строка 473:
</pre>
</pre>


== Настройка mosquitto ==
== Настройка mosquitto == <!--T:72-->
 
<!--T:73-->
UPD:
 
<!--T:74-->
1) openssl.cnf
 
<!--T:75-->
2)  mosquitto=1.4.15-1+wb7-4


<!--T:76-->
3) libateccssl1.1=0.2.1
<!--T:77-->
4) ca-certificates-contactless
<!--T:78-->
5) usermod -a -G i2c mosquitto
<!--T:79-->
<pre>
root@wirenboard-APIJVTIG:~# cat /etc/mosquitto/conf.d/bridge-hw.conf
connection wb_devices_cloud.wirenboard-APIJVTIG
address contactless.ru:8884
<!--T:80-->
bridge_cafile  /etc/ssl/certs/WirenBoard_Root_CA.pem
bridge_certfile /etc/ssl/device/device_bundle.crt.pem
bridge_keyfile  engine:ateccx08:ATECCx08:00:04:C0:00
bridge_capath /etc/ssl/certs/
bridge_insecure true
<!--T:81-->
notifications true
notification_topic /client/wirenboard-APIJVTIG/bridge_status
<!--T:82-->
topic /devices/#  both 2 "" /client/wirenboard-APIJVTIG
topic /config/#  both 2 "" /client/wirenboard-APIJVTIG
topic /rpc/#  both 2 "" /client/wirenboard-APIJVTIG
</pre>
<!--T:83-->
<pre>
root@wirenboard-APIJVTIG:~# cat fix.sh
#!/bin/bash -x
CERT_IN=/etc/ssl/certs/device_bundle.crt.pem
CERT_OUT=/etc/ssl/device/device_bundle.crt.pem
CERT_BKP=/etc/ssl/device/_device_bundle.crt.pem
<!--T:84-->
prn() {
    cat $CERT_IN|grep "$1" -n|sed -n "$2p"|cut -d':' -f1
}
<!--T:85-->
fix() {
    B1=$(prn "BEGIN CERTIFICATE" 1)
    B2=$(prn "BEGIN CERTIFICATE" 2)
    E1=$(prn "END CERTIFICATE" 1)
    E2=$(prn "END CERTIFICATE" 2)
    <!--T:86-->
if [[ "$E1" -le "$B1" || "$E2" -le "$B2" || "$E1" -ge "$B2" ]]; then
        echo "ERROR in device cert bundle."
        exit 1
    fi
    <!--T:87-->
cat $CERT_IN|sed -n "${B2},${E2}p"
    cat $CERT_IN|sed -n "${B1},${E1}p"
}
<!--T:88-->
mkdir -p /etc/ssl/device
<!--T:89-->
if [ -f "$CERT_IN" ]; then
    echo "backup device bundle certificate..."
    cp "$CERT_IN" "$CERT_BKP"
fi
<!--T:90-->
if [ ! -f "$CERT_OUT" ]; then
    if [ ! -f "$CERT_IN" ]; then
        echo "ERROR: no such file: $CERT_IN"
        exit 1
    fi
    fix > "$CERT_OUT"
    echo "Device bundle certificate fix done."
    rm -f "${CERT_IN}"
else
    echo "Device cert $CERT_OUT already fixed."
fi
</pre>
<!--T:91-->
Генерируем приватный ключ и запрос на сертификат:
Генерируем приватный ключ и запрос на сертификат:


<!--T:92-->
<pre>
<pre>
openssl genrsa -out example.key 2048
openssl genrsa -out example.key 2048
Строка 397: Строка 580:
</pre>
</pre>


<!--T:93-->
Создаем сертификат сервера в CA.
Создаем сертификат сервера в CA.
<pre>
<pre>
Строка 402: Строка 586:
</pre>
</pre>


Копируем файлы '''ca.crt''', '''mosquitto.crt''', '''mosquitto.key''' на сервер и редактируем файл конфигурации:
<!--T:94-->
 
Копируем файлы '''ca.crt''', '''mosquitto.crt''', '''mosquitto.key''' на сервер и редактируем файл конфигурации '''/etc/mosquitto/conf.d/server.conf'''
/etc/mosquitto/conf.d/server.conf


<!--T:95-->
<pre>
<pre>
cafile                      /etc/mosquitto/ssl/ca.crt
cafile                      /etc/mosquitto/ssl/ca.crt
Строка 414: Строка 598:
</pre>
</pre>


<!--T:96-->
Запускаем серис:
Запускаем серис:
<pre>
<pre>
Строка 419: Строка 604:
</pre>
</pre>


<!--T:97-->
Также, если требуется, можно сделать чтобы локальный mosquitto сервер на контроллере
Также, если требуется, можно сделать чтобы локальный mosquitto сервер на контроллере
форвардил некоторые топики на удаленный сервер. Для этого создаем файл бриджа: '''/etc/mosquitto/bridge.conf'''
форвардил некоторые топики на удаленный сервер. Для этого создаем файл бриджа: '''/etc/mosquitto/bridge.conf'''


<!--T:98-->
<pre>
<pre>
connection main
connection main
Строка 427: Строка 614:
address example.com:1883
address example.com:1883


<!--T:99-->
bridge_cafile      /etc/mosquitto/certs/ca.crt
bridge_cafile      /etc/mosquitto/certs/ca.crt
bridge_certfile    /etc/mosquitto/certs/device_AP6V5MDG.crt
bridge_certfile    /etc/mosquitto/certs/device_AP6V5MDG.crt
Строка 432: Строка 620:
</pre>
</pre>


<!--T:100-->
После перезапуска локального сервиса mosquitto топики /test/.. будут отправлятся на удаленный сервер example.com
После перезапуска локального сервиса mosquitto топики /test/.. будут отправлятся на удаленный сервер example.com
по защищенному ssl каналу.
по защищенному ssl каналу.


<!--T:101-->
Примеры клиентских команд mosquitto.
Примеры клиентских команд mosquitto.
Отправка сообщения "message" в топик "test" на сервере example.com  
Отправка сообщения "message" в топик "test" на сервере example.com  


<!--T:102-->
<pre>
<pre>
mosquitto_pub -h example.com --cert device_AP6V5MDG.crt --key 'engine:ateccx08:ATECCx08:00:04:C0:00' --cafile ca.crt -t "test" -m "message"  
mosquitto_pub -h example.com --cert device_AP6V5MDG.crt --key 'engine:ateccx08:ATECCx08:00:04:C0:00' --cafile ca.crt -t "test" -m "message"  
</pre>
</pre>


<!--T:103-->
Получение сообщений из топика "test" на сервере example.com
Получение сообщений из топика "test" на сервере example.com


<!--T:104-->
<pre>
<pre>
mosquitto_sub -h example.com --cert device_AP6V5MDG.crt --key 'engine:ateccx08:ATECCx08:00:04:C0:00' -t "test" --cafile ca.crt
mosquitto_sub -h example.com --cert device_AP6V5MDG.crt --key 'engine:ateccx08:ATECCx08:00:04:C0:00' -t "test" --cafile ca.crt
</pre>
</pre>
</translate>

Версия 12:40, 16 октября 2023

Другие языки:

Использование встроенного чипа ATECCx08 для авторизации на внешних сервисах.

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

Используя данную микросхему можно организовать авторизацию контроллера и защиту соединения по SSL на внешних сервисах и быть уверенным, что запрос выполнен с использованием именно этого экземпляра микросхемы.

В данной статье пойдет речь о трех вариантах применения: nginx, openssl, mosquitto.

Для доступа к криптоустройству нужна библиотека libateccssl1.1, установим ее командой:

apt install libateccssl1.1

Далее нужно отредактировать файл /etc/ssl/openssl.cnf, добавив в нем следующие строчки:

openssl_conf = openssl_init

[openssl_init]
engines         = engine_section

[engine_section]
ateccx08 = ateccx08_section

[ateccx08_section]
init = 1

Теперь приступим к созданию сертификатов. Для начала создадим свой центр сертификации (Certification Authority, CA):

Для этого сгенерируем ключевую пару:

openssl genrsa -out ca.key 2048

И сертификат нашего CA:

openssl req -x509 -new -days 3650 -key ca.key -out ca.crt -subj "/CN=MY CA"

CA является основой безопасности в данной схеме, поэтому эти операции выполняем на машине доступ к которой есть только у владельца CA.

Далее на конроллере WB создаем запрос на сертификат устройства:


Для Wiren Board 7:

openssl req -new -engine ateccx08 -keyform engine -key ATECCx08:00:02:C0:00 -subj "/CN=wirenboard-AP6V5MDG" -out device_AP6V5MDG.csr

Для Wiren Board 6:

openssl req -new -engine ateccx08 -keyform engine -key ATECCx08:00:04:C0:00 -subj "/CN=wirenboard-AP6V5MDG" -out device_AP6V5MDG.csr

В этой команде мы указываем, что запрос подписывается приватным ключом, находящимся в криптоустройстве ateccx08 ключом с идентификатором ATECCx08:00:02:C0:00 и публичным именем wirenboard-AP6V5MDG. Запрос помещаем в файл device_AP6V5MDG.csr.

Имя можно выбрать любое уникальное, удобно для этой цели спользовать идентификатор устройства, который по умолчанию прописывается в файле /etc/hostname:

cat /etc/hostname
wirenboard-AP6V5MDG

Далее этот запрос подписываем в нашем центре сертификации: openssl x509 -req -in device_AP6V5MDG.csr -CA ca.crt -CAkey ca.key -out device_AP6V5MDG.crt -days 365 -CAcreateserial

В этой команде мы уазываем файл запроса, и файлы CA необходимые для подписи. В итоге получаем сертификат устройства device_AP6V5MDG.crt. Файл device_AP6V5MDG.crt копируем на контроллер WB, он будет необходим для авторизацци.

Давайте посмотрим, что содержится в файлах сертификатов CA и устройства:

openssl x509 -text -noout -in device_AP6V5MDG.crt

Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            bf:85:29:be:19:67:f5:3e
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = MY CA
        Validity
            Not Before: Feb  4 14:50:14 2019 GMT
            Not After : Feb  4 14:50:14 2020 GMT
        Subject: CN = wirenboard-AP6V5MDG
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:66:80:f6:83:ea:4f:88:a5:05:df:8f:2c:62:f3:
                    ad:71:55:87:7f:ae:12:ae:b1:74:4b:68:68:fd:f7:
                    e0:8a:f4:44:87:45:ab:c1:07:3f:54:2a:a9:ea:c6:
                    71:1d:41:63:67:1b:75:f4:00:42:8d:fd:f6:d5:b6:
                    52:38:e8:5a:a9
                ASN1 OID: prime256v1
                NIST CURVE: P-256
    Signature Algorithm: sha256WithRSAEncryption
         be:d1:f8:04:fb:34:a9:84:ff:25:b6:04:04:c0:f1:1d:4a:a4:
         04:b8:54:6c:a8:46:61:5f:6c:e7:ab:16:8f:ae:45:46:02:99:
         c6:d3:90:42:91:20:c7:89:d5:cf:4e:23:3a:33:64:ab:1b:c9:
         78:18:82:f4:39:8b:97:ae:6c:ee:a4:13:0c:5a:54:6b:69:c8:
         1e:fa:24:3d:48:2c:ea:0e:5c:0d:c3:43:c2:49:ea:b2:f8:5e:
         d7:0b:b5:4e:67:87:53:84:76:23:aa:10:77:5d:f1:21:9e:b0:
         4b:16:99:7c:d4:d3:d6:e7:00:9c:bf:53:a1:4b:f4:2c:fc:0b:
         64:10:fb:77:fc:3d:b2:71:cf:be:0b:b1:a2:62:ed:8c:92:e4:
         78:73:dc:69:c4:61:10:22:66:11:11:8b:d4:3c:b6:4f:7f:2c:
         24:07:61:47:15:2a:56:7e:71:69:59:15:8b:53:c8:e2:b5:ed:
         34:a0:78:70:d4:f6:cf:0f:6d:df:45:00:3b:0a:39:a2:fb:e7:
         89:f3:d9:88:7f:6b:bd:fa:ca:5e:44:94:74:70:5e:86:0b:93:
         ca:16:71:42:67:eb:77:bd:15:e3:90:2f:68:fd:bc:61:25:a3:
         a6:e7:8b:b1:42:bc:c2:36:d4:17:67:b3:77:fb:bd:06:e9:35:
         3b:8e:08:48

Видим, что сертификат выписан Центром сертификации с именем "MY CA" на 1 год, начная с "Feb 4 14:50:14 2019 GMT" (для этого мы указывали -days 365 в команде подписи), устройству с именем wirenboard-AP6V5MDG, имеющим приватный ключ соответствующий публичному Subject Public Key Info. Сертификат подписан цифровой подписью.

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

openssl x509 -text -noout -in ca.crt

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            d9:e0:91:e7:d0:27:02:db
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = MY CA
        Validity
            Not Before: Feb  4 14:30:01 2019 GMT
            Not After : Feb  1 14:30:01 2029 GMT
        Subject: CN = MY CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c8:5e:02:b8:55:e5:42:97:f7:c6:53:61:d3:df:
                    66:bf:05:dd:7a:0c:61:a4:68:36:23:3f:3b:c7:83:
                    ec:47:9b:5a:ed:78:8a:5b:f1:5f:88:3d:36:f2:3e:
                    7b:84:9e:1b:e5:87:bf:3b:00:33:36:1c:0b:3a:16:
                    2f:8d:be:0e:a4:9e:25:73:4d:93:8a:47:74:29:65:
                    0e:4e:ea:44:fd:c4:c0:bf:fa:bc:11:d5:93:43:e2:
                    65:18:bb:f7:e5:fc:16:8c:f9:11:97:76:2c:bb:cb:
                    c0:94:7e:78:12:20:c9:8a:68:29:c1:e8:af:7e:d7:
                    63:6e:a3:57:79:c9:b3:a8:8c:a3:2d:3e:15:1a:25:
                    ea:f1:50:fc:ea:93:8f:14:5f:34:61:07:a9:dc:24:
                    b8:11:de:9c:17:13:03:19:0d:0c:a3:e8:10:31:50:
                    82:5b:cb:0e:26:d5:b1:fe:df:c3:f6:f9:e4:0f:b1:
                    24:40:f2:8d:95:d5:ea:34:b3:27:a1:87:76:9d:f2:
                    65:74:d5:40:47:dd:a1:32:46:c3:37:ec:a5:b3:09:
                    30:73:99:d1:9c:bb:a8:05:61:2a:56:89:32:5e:c0:
                    5d:1d:b6:a6:b6:74:17:be:74:69:9c:b0:e3:bc:b4:
                    f9:96:6d:aa:60:ae:70:d1:ee:07:e5:2c:5d:0a:af:
                    ce:b3
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                53:4B:6D:7A:1A:1F:F8:BE:3F:59:64:70:2B:31:2F:7A:7F:2A:6B:14
            X509v3 Authority Key Identifier: 
                keyid:53:4B:6D:7A:1A:1F:F8:BE:3F:59:64:70:2B:31:2F:7A:7F:2A:6B:14

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         30:c2:f3:e0:96:51:7d:13:be:06:1d:40:06:70:b8:36:e9:46:
         81:64:0c:f0:e7:69:6f:31:2c:e1:86:df:f8:ad:b2:84:6e:90:
         4a:38:48:7d:ae:92:a5:71:40:c8:8e:0f:7e:67:e5:66:e7:70:
         4d:52:92:fa:a6:54:45:3b:a6:b9:b3:f4:35:ad:1c:6e:6e:15:
         06:81:ef:13:54:80:89:2e:7d:75:06:22:59:89:44:a9:ad:25:
         30:6c:02:e1:3b:2e:e2:bc:46:90:d1:a5:00:eb:87:57:60:a4:
         cf:e0:03:4a:b5:32:c4:dc:c7:e3:34:d4:c8:af:e3:ce:20:8c:
         c4:7f:f0:b8:72:d7:65:3b:38:be:2b:b1:d0:4e:9e:e2:52:32:
         41:fe:22:d2:7c:13:60:fe:4a:13:4b:c5:09:f0:00:89:32:22:
         47:4d:2c:a1:21:8e:b2:7d:0f:1a:10:f5:94:ee:fb:18:d3:15:
         f1:9e:70:89:73:c4:41:71:0e:92:22:9c:18:ef:0b:b1:7c:42:
         41:e7:9f:e7:82:d5:db:f3:60:d3:2f:2a:86:e4:0c:c0:4c:0c:
         17:12:ec:e4:37:96:dc:2d:01:00:22:ac:b5:33:6b:97:41:d7:
         37:e5:75:fa:c9:6b:00:2a:d8:87:0f:9e:f3:aa:c5:23:4e:60:
         02:a9:5b:eb

Видим, что сертификат подписан сам своим-же приватным ключом (Issuer: CN = MY CA, Subject: CN = MY CA) и выписан на 10 лет: начная с "Feb 4 14:30:01 2019 GMT" (для этого мы указывали -days 3650 в команде подписи)

Таким образом цепочка доверия (проверки) выстраивается следующим образом:

Сертификат устройства подписан сертификатом CA и может быть им проверен:

openssl verify -CAfile ca.crt device_AP6V5MDG.crt 
device_AP6V5MDG.crt: OK

Ну а сам сертификат подписан "собой":

openssl verify -CAfile ca.crt ca.crt
ca.crt: OK

Проверка сертификатов на сервере с помощью nginx .

Допустим в интернете есть сервер, который должен обрабатывать запросы только от устройств обладающих сертификатами, выписанными нашим CA. Для включения такой проверки в конфигурационном файле nginx необходимо в секции http или server прописать следующие строчки:

  ssl_client_certificate  ca.crt;
  ssl_verify_client       on;

Теперь nginx будет требовать от клиента сертификат, который должен проходить проверку с помощью ca.crt, иначе сервер вернет клиенту ошибку 400:

curl https://example.com
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.14.0 (Ubuntu)</center>
</body>
</html>

Nginx в такой конфигурации удобно использовать на сервере перед реальным сервисом, которые не поддерживают проверку клиентских сертификатов. Для этого нужно настроить proxy_pass на сам сервис и, например, добавить в заголовки серийный номер контроллера через переменную $ssl_client_s_dn или весь клиентский сертификат целиком.

Добавление поддержки клиентских сертификатов к произвольным сервисам на контроллере

Часто программы на контролелере не поддерживают TLS совсем, либо не поддерживают указание клиентских сертификатов, либо не поддерживают работу с openssl engine. Можно отправить обращения от таких программ к серверу сквозь локальный прокси, который будет оборачивать трафик в TLS с использованием аппаратных ключей.

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

Теперь сделаем так, чтобы HTTP запросы с контроллера WB проходили данную проверку. Для простоты будем использовать nginx и на клиентской стороне. Это даст возможность работать с защищенными серверами клиентам не умеющим делать SSL соединения.

Для начала создадим на неиспользуемом локальном порту http сервер, который будет делать всю https "магию" за нас:

server {
    listen 8080;
    location / { 
        proxy_pass                 https://example.com;
        proxy_ssl_name             example.com;
        proxy_ssl_server_name      on; 
        proxy_ssl_certificate      device_AP6V5MDG.crt;
        proxy_ssl_certificate_key  engine:ateccx08:ATECCx08:00:04:C0:00;
    }   
}

Добавим пользователя www-data в группу i2c для доступа к криптоустройству:

usermod -G www-data

Выполним команду service nginx restart для обновления конфигурации.

Теперь при обращении по http на локальный порт 8080 зашифрованные запросы с аутентификационной информацией будут отправлятся на сервер example.com.

curl localhost:8080/
<html>
<body>
EXAMPLE.COM
</body>
</html>

Настройка openvpn

Для начала установим пакет:

apt install openvpn

Cоздадим файл req.cnf - он нам потребуется для создания сертификата сервера.

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth

Серверу openvpn также требуется файл с параметрами DH, сделаем его.

openssl dhparam -out dh2048.pem 2048

Создание данного файла может потребовать несколько минут.

Далее сделаем приватный ключ нашего сервера и запрос на сертификат. Предполагаем, для примера, что сервер имеет имя example.com:

 
openssl genrsa -out example.key 2048

openssl req -new -key example.key -subj "/CN=example.com" -out example.csr

Подписываем запрос в нашем центре сертификации:

openssl x509 -req -in example.csr -CA ca.crt -CAkey ca.key -out example.crt -days 365 -CAcreateserial -extfile req.cnf -extensions v3_req

Теперь приступим к настройке openvpn сервера на машине example.com.

Копируем файлы ca.crt, example.crt, example.key и dh2048.pem и редактируем файл конфигурации openvpn сервера. По умолчанию конфигурационный файл лежит в файле /etc/openvpn/server.conf

port 1194
proto tcp 
dev tun 
ca          /etc/openvpn/server/ca.crt
cert        /etc/openvpn/server/example.crt
key         /etc/openvpn/server/example.key
dh          /etc/openvpn/server/dh2048.pem
topology subnet
server 10.8.0.0 255.255.255.0
keepalive 10 60
key-direction 0
cipher AES-128-CBC
auth SHA256
comp-lzo
user nobody
group nogroup
persist-key
persist-tun
status /var/run/openvpn/openvpn-status.log
management 127.0.0.1 7500
log-append  /var/log/openvpn.log

Добавим пользователя openvpn в группу i2c для доступа к криптоустройству:

usermod -G www-data

после этого запускаем сервер командой service openvpn start.

Далее на контроллере создаем файл конфигурации клиента: client.ovpn:

-------------------------------------------------------------------------
client
dev tun
proto tcp
remote example.com 1194
resolv-retry infinite
nobind
user openvpn
'''group i2c'''
persist-key
persist-tun
cipher AES-128-CBC
auth SHA256
key-direction 1
keepalive 1 10
remote-cert-tls server
comp-lzo
-------------------------------------------------------------------------

Обратите внимание на строчку group i2c. Она необходима для работы с криптоустройством.

После этого запускаем клиента:

openvpn --config example.ovpn --ca ca.crt --cert device_AP6V5MDG.crt --key engine:ateccx08:ATECCx08:00:04:C0:00

Если все хорошо, то в системе должен появиться интерфейс tun0 с адресом из подсети 10.8.0.0/24:

tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
        inet 10.8.0.2  netmask 255.255.255.0  destination 10.8.0.2
        inet6 fe80::53b0:83f3:b1f5:b817  prefixlen 64  scopeid 0x20<link>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 100  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6  bytes 288 (288.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Для проверки работоспособности запускаем ping:

ping 10.8.0.1
PING 10.8.0.1 (10.8.0.1) 56(84) bytes of data.
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=2.23 ms
64 bytes from 10.8.0.1: icmp_seq=2 ttl=64 time=2.83 ms

Настройка mosquitto

UPD:

1) openssl.cnf

2) mosquitto=1.4.15-1+wb7-4

3) libateccssl1.1=0.2.1

4) ca-certificates-contactless

5) usermod -a -G i2c mosquitto

root@wirenboard-APIJVTIG:~# cat /etc/mosquitto/conf.d/bridge-hw.conf 
connection wb_devices_cloud.wirenboard-APIJVTIG
address contactless.ru:8884

bridge_cafile   /etc/ssl/certs/WirenBoard_Root_CA.pem
bridge_certfile /etc/ssl/device/device_bundle.crt.pem
bridge_keyfile  engine:ateccx08:ATECCx08:00:04:C0:00
bridge_capath /etc/ssl/certs/
bridge_insecure true

notifications true
notification_topic /client/wirenboard-APIJVTIG/bridge_status

topic /devices/#  both 2 "" /client/wirenboard-APIJVTIG
topic /config/#  both 2 "" /client/wirenboard-APIJVTIG
topic /rpc/#  both 2 "" /client/wirenboard-APIJVTIG
root@wirenboard-APIJVTIG:~# cat fix.sh 
#!/bin/bash -x 
CERT_IN=/etc/ssl/certs/device_bundle.crt.pem
CERT_OUT=/etc/ssl/device/device_bundle.crt.pem
CERT_BKP=/etc/ssl/device/_device_bundle.crt.pem

prn() {
    cat $CERT_IN|grep "$1" -n|sed -n "$2p"|cut -d':' -f1
}

fix() {
    B1=$(prn "BEGIN CERTIFICATE" 1)
    B2=$(prn "BEGIN CERTIFICATE" 2)
    E1=$(prn "END CERTIFICATE" 1)
    E2=$(prn "END CERTIFICATE" 2)

    if [[ "$E1" -le "$B1" || "$E2" -le "$B2" || "$E1" -ge "$B2" ]]; then
        echo "ERROR in device cert bundle."
        exit 1
    fi

    cat $CERT_IN|sed -n "${B2},${E2}p"
    cat $CERT_IN|sed -n "${B1},${E1}p"
}

mkdir -p /etc/ssl/device

if [ -f "$CERT_IN" ]; then
    echo "backup device bundle certificate..."
    cp "$CERT_IN" "$CERT_BKP"
fi

if [ ! -f "$CERT_OUT" ]; then
    if [ ! -f "$CERT_IN" ]; then
        echo "ERROR: no such file: $CERT_IN"
        exit 1
    fi
    fix > "$CERT_OUT"
    echo "Device bundle certificate fix done."
    rm -f "${CERT_IN}"
else
    echo "Device cert $CERT_OUT already fixed."
fi


Генерируем приватный ключ и запрос на сертификат:

openssl genrsa -out example.key 2048
openssl req -new -key example.key -subj "/CN=example.com" -out example.csr

Создаем сертификат сервера в CA.

openssl x509 -req -in example.csr -CA ca.crt -CAkey ca.key -out example.crt -days 365 -CAcreateserial -extfile req.cnf -extensions v3_req

Копируем файлы ca.crt, mosquitto.crt, mosquitto.key на сервер и редактируем файл конфигурации /etc/mosquitto/conf.d/server.conf

cafile                      /etc/mosquitto/ssl/ca.crt
certfile                    /etc/mosquitto/ssl/mosquitto.crt
keyfile                     /etc/mosquitto/ssl/mosquitto.key
require_certificate         true
use_identity_as_username    true

Запускаем серис:

service mosquitto start

Также, если требуется, можно сделать чтобы локальный mosquitto сервер на контроллере форвардил некоторые топики на удаленный сервер. Для этого создаем файл бриджа: /etc/mosquitto/bridge.conf

connection main
topic test/# out
address example.com:1883

bridge_cafile       /etc/mosquitto/certs/ca.crt
bridge_certfile     /etc/mosquitto/certs/device_AP6V5MDG.crt
bridge_keyfile      engine:ateccx08:ATECCx08:00:04:C0:00

После перезапуска локального сервиса mosquitto топики /test/.. будут отправлятся на удаленный сервер example.com по защищенному ssl каналу.

Примеры клиентских команд mosquitto. Отправка сообщения "message" в топик "test" на сервере example.com

mosquitto_pub -h example.com --cert device_AP6V5MDG.crt --key 'engine:ateccx08:ATECCx08:00:04:C0:00' --cafile ca.crt -t "test" -m "message" 

Получение сообщений из топика "test" на сервере example.com

mosquitto_sub -h example.com --cert device_AP6V5MDG.crt --key 'engine:ateccx08:ATECCx08:00:04:C0:00' -t "test" --cafile ca.crt