使用 OpenSSL 滚动我自己的 SSL,不工作
Rolling my own SSL with OpenSSL, not working
我已经阅读了所有关于不这样做的注意事项,但是......我需要这样做。我需要能够从 C 程序向 SSL/TLS 安全服务器发送 POST 消息,但它无法正常工作。
我使用相同的代码通过 SSL 获取数据,但我不能 POST。每次尝试 returns 一个 400 BAD REQUEST。我可以通过另一个 ReST 客户端(奇怪的是称为 "Rest Client")使用完全相同的数据并且它有效。我可以通过 curl 运行 相同的命令并且它有效。但是通过 C/openssl,相同的响应。
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
/* https://www.openssl.org/docs/ssl/SSL_CTX_new.html */
const SSL_METHOD* method = SSLv23_method();
/* http://www.openssl.org/docs/ssl/ctx_new.html */
ctx = SSL_CTX_new(method);
/* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
/* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
SSL_CTX_set_verify_depth(ctx, 5);
const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
long old_opts = SSL_CTX_set_options(ctx, flags);
UNUSED(old_opts);
/* http://www.openssl.org/docs/ssl/SSL_CTX_set_default_verify_paths.html */
res = SSL_CTX_set_default_verify_paths(ctx);
/* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
web = BIO_new_ssl_connect(ctx);
/* https://www.openssl.org/docs/crypto/BIO_s_connect.html */
res = BIO_set_conn_hostname(web, HOST_NAME ":" HOST_PORT);
/* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
/* This copies an internal pointer. No need to free. */
BIO_get_ssl(web, &ssl);
/* https://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_CONTEXTS */
/* https://www.openssl.org/docs/ssl/SSL_CTX_set_cipher_list.html */
res = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS);
/* No documentation. See the source code for tls.h and s_client.c */
res = SSL_set_tlsext_host_name(ssl, HOST_NAME);
/* https://www.openssl.org/docs/crypto/BIO_s_file.html */
out = BIO_new_fp(stdout, BIO_NOCLOSE);
/* https://www.openssl.org/docs/crypto/BIO_s_connect.html */
res = BIO_do_connect(web);
/* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
res = BIO_do_handshake(web);
/* Step 1: verify a server certifcate was presented during negotiation */
/* https://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html */
X509* cert = SSL_get_peer_certificate(ssl);
/* Step 2: verify the result of chain verifcation */
/* http://www.openssl.org/docs/ssl/SSL_get_verify_result.html */
/* Error codes: http://www.openssl.org/docs/apps/verify.html */
res = SSL_get_verify_result(ssl);
certname = X509_NAME_new();
certname = X509_get_subject_name(cert);
BIO_printf(out, "Displaying the certificate subject data:\n");
X509_NAME_print_ex(out, certname, 0, 0);
BIO_printf(out, "\n");
sprintf(message, "POST /api/buckets\r\nHTTP/1.1\r\nX-IS-AccessKey: accessKey\r\nContent-Type: application/json\r\nContent-Length: 66\r\n\r\n{\"bucketKey\": \"MyBucket\", \"bucketName\": \"My Bucket\"}\r\n\r\n");
BIO_puts(web, message);
BIO_puts(out, message);
int len = 0;
do {
char buff[1536] = {};
/* https://www.openssl.org/docs/crypto/BIO_read.html */
len = BIO_read(web, buff, sizeof(buff));
if(len > 0)
BIO_write(out, buff, len);
/* BIO_should_retry returns TRUE unless there's an */
/* error. We expect an error when the server */
/* provides the response and closes the connection. */
} while (len > 0 || BIO_should_retry(web));
ret = 0;
} while (0);
事实上,这确实成功地打开了一个连接,并检索了一个证书,并验证了它,等等。
verify_callback (depth=1)(preverify=0)
Issuer (cn): DigiCert Global Root CA
Subject (cn): DigiCert SHA2 Secure Server CA
Error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
verify_callback (depth=0)(preverify=1)
Issuer (cn): DigiCert SHA2 Secure Server CA
Subject (cn): *.initialstate.com
Subject (san): *.initialstate.com
Subject (san): initialstate.com
Displaying the certificate subject data:
C=US, ST=TN, L=Brentwood, O=Initial State Technologies, Inc, CN=*.initialstate.com
这似乎行得通。如果我从 www.google.com 执行一个简单的 "GET /",我会得到相同的证书响应(好吧,使用来自 google 的适当证书)并且我可以很好地恢复所有数据。
所以这似乎与实际 POSTing 数据有关。
如果有人能指出我方法的错误——不仅仅是我这样做的愚蠢行为! -- 非常感谢!
..."POST /api/buckets\r\nHTTP/1.1\r\n
路径和 HTTP 版本之间的换行符错误,必须是 space。这意味着你发送到这里
POST /api/buckets
HTTP/1.1
而不是
POST /api/buckets HTTP/1.1
我已经阅读了所有关于不这样做的注意事项,但是......我需要这样做。我需要能够从 C 程序向 SSL/TLS 安全服务器发送 POST 消息,但它无法正常工作。
我使用相同的代码通过 SSL 获取数据,但我不能 POST。每次尝试 returns 一个 400 BAD REQUEST。我可以通过另一个 ReST 客户端(奇怪的是称为 "Rest Client")使用完全相同的数据并且它有效。我可以通过 curl 运行 相同的命令并且它有效。但是通过 C/openssl,相同的响应。
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
/* https://www.openssl.org/docs/ssl/SSL_CTX_new.html */
const SSL_METHOD* method = SSLv23_method();
/* http://www.openssl.org/docs/ssl/ctx_new.html */
ctx = SSL_CTX_new(method);
/* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
/* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
SSL_CTX_set_verify_depth(ctx, 5);
const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
long old_opts = SSL_CTX_set_options(ctx, flags);
UNUSED(old_opts);
/* http://www.openssl.org/docs/ssl/SSL_CTX_set_default_verify_paths.html */
res = SSL_CTX_set_default_verify_paths(ctx);
/* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
web = BIO_new_ssl_connect(ctx);
/* https://www.openssl.org/docs/crypto/BIO_s_connect.html */
res = BIO_set_conn_hostname(web, HOST_NAME ":" HOST_PORT);
/* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
/* This copies an internal pointer. No need to free. */
BIO_get_ssl(web, &ssl);
/* https://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_CONTEXTS */
/* https://www.openssl.org/docs/ssl/SSL_CTX_set_cipher_list.html */
res = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS);
/* No documentation. See the source code for tls.h and s_client.c */
res = SSL_set_tlsext_host_name(ssl, HOST_NAME);
/* https://www.openssl.org/docs/crypto/BIO_s_file.html */
out = BIO_new_fp(stdout, BIO_NOCLOSE);
/* https://www.openssl.org/docs/crypto/BIO_s_connect.html */
res = BIO_do_connect(web);
/* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
res = BIO_do_handshake(web);
/* Step 1: verify a server certifcate was presented during negotiation */
/* https://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html */
X509* cert = SSL_get_peer_certificate(ssl);
/* Step 2: verify the result of chain verifcation */
/* http://www.openssl.org/docs/ssl/SSL_get_verify_result.html */
/* Error codes: http://www.openssl.org/docs/apps/verify.html */
res = SSL_get_verify_result(ssl);
certname = X509_NAME_new();
certname = X509_get_subject_name(cert);
BIO_printf(out, "Displaying the certificate subject data:\n");
X509_NAME_print_ex(out, certname, 0, 0);
BIO_printf(out, "\n");
sprintf(message, "POST /api/buckets\r\nHTTP/1.1\r\nX-IS-AccessKey: accessKey\r\nContent-Type: application/json\r\nContent-Length: 66\r\n\r\n{\"bucketKey\": \"MyBucket\", \"bucketName\": \"My Bucket\"}\r\n\r\n");
BIO_puts(web, message);
BIO_puts(out, message);
int len = 0;
do {
char buff[1536] = {};
/* https://www.openssl.org/docs/crypto/BIO_read.html */
len = BIO_read(web, buff, sizeof(buff));
if(len > 0)
BIO_write(out, buff, len);
/* BIO_should_retry returns TRUE unless there's an */
/* error. We expect an error when the server */
/* provides the response and closes the connection. */
} while (len > 0 || BIO_should_retry(web));
ret = 0;
} while (0);
事实上,这确实成功地打开了一个连接,并检索了一个证书,并验证了它,等等。
verify_callback (depth=1)(preverify=0)
Issuer (cn): DigiCert Global Root CA
Subject (cn): DigiCert SHA2 Secure Server CA
Error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
verify_callback (depth=0)(preverify=1)
Issuer (cn): DigiCert SHA2 Secure Server CA
Subject (cn): *.initialstate.com
Subject (san): *.initialstate.com
Subject (san): initialstate.com
Displaying the certificate subject data:
C=US, ST=TN, L=Brentwood, O=Initial State Technologies, Inc, CN=*.initialstate.com
这似乎行得通。如果我从 www.google.com 执行一个简单的 "GET /",我会得到相同的证书响应(好吧,使用来自 google 的适当证书)并且我可以很好地恢复所有数据。
所以这似乎与实际 POSTing 数据有关。
如果有人能指出我方法的错误——不仅仅是我这样做的愚蠢行为! -- 非常感谢!
..."POST /api/buckets\r\nHTTP/1.1\r\n
路径和 HTTP 版本之间的换行符错误,必须是 space。这意味着你发送到这里
POST /api/buckets
HTTP/1.1
而不是
POST /api/buckets HTTP/1.1