Шифры EXP (ORT) и M2Crypto/OpenSSL
Мне сложно работать с SSLServer M2Crypto с шифрами класса EXPORT.
Низкие/средние/высокоуровневые шифры работают без проблем, но ЭКСПОРТ просто не будет. Кроме того, когда OpenSSL запускается в режиме сервера из командной строки, он без проблем принимает классы класса EXPORT.
Итак, либо я что-то упустил, либо возникла проблема в модуле M2Crypto. Любая помощь приветствуется.
Используемый код python (ssl-server.py
) выглядит следующим образом:
import M2Crypto
import socket
CERTFILE = "dummy_cert.pem"
KEYFILE = "dummy_key.pem"
PROTOCOL = "sslv3"
HOST = "0.0.0.0"
PORT = 4433
def main():
print "[i] Initializing context ..."
ctx = M2Crypto.SSL.Context(protocol=PROTOCOL, weak_crypto=True)
ctx.load_cert_chain(certchainfile=CERTFILE, keyfile=KEYFILE)
ctx.set_options(M2Crypto.m2.SSL_OP_ALL)
ctx.set_cipher_list("ALL")
print "[i] Initializing socket ..."
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, PORT))
sock.listen(1)
conn, addr = sock.accept()
print "[i] SSL handshake ..."
ssl_conn = M2Crypto.SSL.Connection(ctx=ctx, sock=conn)
ssl_conn.setup_ssl()
try:
ssl_conn_res = ssl_conn.accept_ssl()
except Exception, ex:
print "[x] SSL connection failed: '%s'" % str(ex)
else:
if ssl_conn_res == 1:
print "[i] SSL connection accepted"
else:
print "[x] SSL handshake failed: '%s'" % ssl_conn.ssl_get_error(ssl_conn_res)
if __name__ == "__main__":
main()
Симптомы:
$ uname -a
Linux XYZ 2.6.38-15-generic #59-Ubuntu SMP Fri Apr 27 16:03:32 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=11.04
DISTRIB_CODENAME=natty
DISTRIB_DESCRIPTION="Ubuntu 11.04"
$ python -c "import M2Crypto;print M2Crypto.version_info"
(0, 20, 1)
$ openssl version
OpenSSL 0.9.8o 01 Jun 2010
1) NOT OK
SERVER (terminal 1): $ python ssl-server.py
CLIENT (terminal 2): $ openssl s_client -connect localhost:4433 -cipher EXPORT
CONNECTED(00000003)
28131:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:602:
2) OK
SERVER (terminal 1): $ openssl s_server -cert dummy_cert.pem -key dummy_key.pem -ssl3 -no_tls1 -no_ssl2 -cipher EXPORT
CLIENT (terminal 2): $ openssl s_client -connect localhost:4433 -cipher EXPORT
CONNECTED(00000003)
depth=0 C = BE, CN = www.example.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = BE, CN = www.example.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 C = BE, CN = www.example.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/C=BE/CN=www.example.com
i:/C=BE/CN=test-ca
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/C=BE/CN=www.example.com
issuer=/C=BE/CN=test-ca
---
No client certificate CA names sent
---
SSL handshake has read 1141 bytes and written 242 bytes
---
New, TLSv1/SSLv3, Cipher is EXP-EDH-RSA-DES-CBC-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: zlib compression
Expansion: zlib compression
SSL-Session:
Protocol : SSLv3
Cipher : EXP-EDH-RSA-DES-CBC-SHA
Session-ID: B052D5D5A436F9A0B9D3FB24F2E32A8A06A0B6828230621C4CFAEB82A0A9AE0C
Session-ID-ctx:
Master-Key: 47F6E3720D06518B961FE389F13BCDE42C37F703099ABBB9B3DA35383C420F519D4F4773D35E470CF6FF7BB243B29069
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Compression: 1 (zlib compression)
Start Time: 1340644713
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---
Содержание a dummy_cert.pem
выглядит следующим образом:
-----BEGIN CERTIFICATE-----
MIICkTCCAfqgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAfMQswCQYDVQQGEwJCRTEQ
MA4GA1UEAxMHdGVzdC1jYTAeFw0xMjA1MDYwODQyNDlaFw0yMjA1MDMwODQyNDla
MCcxCzAJBgNVBAYTAkJFMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wgZ8wDQYJ
KoZIhvcNAQEBBQADgY0AMIGJAoGBAL7OBv9wRwtNjN984XSy22/rw6tHM6Lq/Ccf
NoHKbqwC+PsxgmgJJiGBGewrzBR42toqHJi7EjHhuvrgqV9s2duPQBAANh7tzY1h
6VekrwhIIt4o1h0F2KB16VXA8s918d+8pRGt2T11GUh/QT3m9yY1VzqdIBeAfklC
ET6ncPK/AgMBAAGjgdQwgdEwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw
KwYJYIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYD
VR0OBBYEFNGQArEZPKprJTn7A64qEFfl0m4xME8GA1UdIwRIMEaAFFuITOUJlGrJ
9lKufs8cm1MpwXrroSOkITAfMQswCQYDVQQGEwJCRTEQMA4GA1UEAxMHdGVzdC1j
YYIJALimgW7YUgdrMAkGA1UdEgQCMAAwCQYDVR0RBAIwADANBgkqhkiG9w0BAQUF
AAOBgQDWh8A0eBxI9XHy68xdjFsk2oerJeV6qqlcmtPZgz3GlarRcWcKsRJOyLLL
dCOe7tY5isWQAoLt6XALzDWjbQkTJnxBaKHif1MIikuajaYKT7LA1MvFn50Qrm6n
f9hG7gvdTpm1rlPcs0qibp1vJVubkU51mT6JT4UnLfeVIjtL7Q==
-----END CERTIFICATE-----
Содержание a dummy_key.pem
выглядит следующим образом:
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQC+zgb/cEcLTYzffOF0sttv68OrRzOi6vwnHzaBym6sAvj7MYJo
CSYhgRnsK8wUeNraKhyYuxIx4br64KlfbNnbj0AQADYe7c2NYelXpK8ISCLeKNYd
BdigdelVwPLPdfHfvKURrdk9dRlIf0E95vcmNVc6nSAXgH5JQhE+p3DyvwIDAQAB
AoGBAIZldIRkP4Z0n2+j9OJQQUS6Wl7AjlyJDAc6cxhE0GOUzG+S1foVx6f92ZaC
2wLoha75zp691fkQuLWRnXu7nk9QwxQdOppKijIPHdL2cYtUc9UCedN5rExjpcOP
4Hjwf17YOxK2J0zzmG1djTBB47BKGUedSQ7E1QxGcrESS2XxAkEA+6ey2jy8etWi
QmCdJJIxXwKRVHCmt5LVwj+IOk/u3sr1AGfBm7spKGU3boCiFt4FmjGMax7B9r/e
zPaMb34guwJBAMIZX7Vv5gfjvWtgp6pyE/UkjRSOKBpuy9gyiqtLBJwehj/qsBqr
O6tFmjMFiudVusnVSrEFGAPLV52xf0U4580CQQDkEQ1UH2spX2dYBLslo6A+3NLc
1eMhx18WVgGd50cyfnkfzuh1vF8GjwR3jvhXBQvKvFDn284pU6YV1vNbL9F1AkEA
o2CwSwyRV3q+6i9Fchbr7aCCkBbIctdoBeclCeHvU2nuHsbwzMHtS9EeZmv365kh
zNoYMMDU4fy7FyVct2ua0QJASXtIwYKZ2CAP+lAQqfh+knRRqtqdLt4Lt0mpML5m
UtsECS8frKeF3mynXfsyRkvC8F2WFiJVJ3+D+y3zYNGlZg==
-----END RSA PRIVATE KEY-----
Обновление: при использовании пакетов подтверждения низкого уровня, похоже, одинаковы, за исключением того, что поле random[32]
делает это еще более странным.
Дамп SSL (ssldump -a -A -H -i lo
) для обоих случаев можно найти здесь:
http://pastebin.com/YuC7d8zg (НЕ ОК)
http://pastebin.com/U6YGQmv9 (пример OK)
Ответы
Ответ 1
Мне понадобились следующие две настройки для python script, чтобы он работал с наборами экспортных шифров:
PROTOCOL = "sslv23"
...
print "[i] Initializing context ..."
ctx = M2Crypto.SSL.Context(protocol=PROTOCOL, weak_crypto=True)
ctx.load_cert_chain(certchainfile=CERTFILE, keyfile=KEYFILE)
ctx.set_options(M2Crypto.m2.SSL_OP_ALL)
ctx.set_tmp_rsa(M2Crypto.RSA.gen_key(512, 65537))
ctx.set_cipher_list("ALL")
То есть:
- Использовать SSLv23 в качестве идентификатора протокола (совместимый режим SSLv2/v3). Не уверен, почему это необходимо в этом случае, но, похоже, это не работает.
- Установите временный, эфемерный ключ RSA в контексте с помощью
set_tmp_rsa()
. Это необходимо, потому что с помощью экспортных шифров предоставленный (1024-разрядный) ключ RSA используется только для аутентификации (подписи), а временный, поврежденный экспортю 512-разрядный ключ RSA используется для обеспечения конфиденциальности (шифрования). OpenSSL требует, чтобы вы установили этот ключ в контекст (см. документацию SSL_set_tmp_rsa()).
Как ни странно, он также работает только в режиме SSLv2 (при тестировании -ssl2
on openssl s_client
) без установки временного ключа RSA (вызов set_tmp_rsa
закомментирован в script). Я понятия не имею, почему.
В целом, некоторые комплекты шифров требуют добавления специальных ключей в контекст, например. с использованием DH (параметры группы) или ECDH (кривая). Чтобы точно увидеть, что используется для каждого набора шифров, openssl ciphers -v
может быть проницательным, например:
% openssl ciphers -v EXPORT
EXP-EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH(512) Au=RSA Enc=DES(40) Mac=SHA1 export
EXP-EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH(512) Au=DSS Enc=DES(40) Mac=SHA1 export
EXP-ADH-DES-CBC-SHA SSLv3 Kx=DH(512) Au=None Enc=DES(40) Mac=SHA1 export
EXP-DES-CBC-SHA SSLv3 Kx=RSA(512) Au=RSA Enc=DES(40) Mac=SHA1 export
EXP-RC2-CBC-MD5 SSLv3 Kx=RSA(512) Au=RSA Enc=RC2(40) Mac=MD5 export
EXP-RC2-CBC-MD5 SSLv2 Kx=RSA(512) Au=RSA Enc=RC2(40) Mac=MD5 export
EXP-ADH-RC4-MD5 SSLv3 Kx=DH(512) Au=None Enc=RC4(40) Mac=MD5 export
EXP-RC4-MD5 SSLv3 Kx=RSA(512) Au=RSA Enc=RC4(40) Mac=MD5 export
EXP-RC4-MD5 SSLv2 Kx=RSA(512) Au=RSA Enc=RC4(40) Mac=MD5 export
ИЗМЕНИТЬ в ответ на вопрос о наборах шифрования DSS:
Для наборов шифрования DSS/DSA требуются параметры DH и, конечно же, сертификат сервера на основе DSS/DSA вместо (только) RSA. Это справедливо не только для наборов экспортных шифров, но и для всех наборов с использованием DSS/DSA для аутентичности. DSS/DSA могут быть разработаны только для подписей, а не для шифрования, чтобы обеспечить экспорт в ненадежные страны. Поскольку DSS/DSA может использоваться только для подписей, ему нужен эфемерный обмен ключами Диффи-Хеллмана для создания общего сеансового ключа. Это то, что означает EDH в наборе шифров. Чтобы настроить параметры DH, вы должны использовать эквиваленты M2Crypto API Open SSL_set_tmp_dh().
Обратите внимание, что OpenSSL позволяет загружать RSA и DSA/DSS cert/keypair в один и тот же контекст SSL.