- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在寻找一种方法,使用 OpenSSL API 在服务器端将客户端证书限制为特定的一组自签名证书。
有一组可信的自签名证书,比如 ./dir/*.pem
。如果他们不提供这些证书之一,我想拒绝连接。
我可以通过在 SSL 上下文验证回调中比较服务器和客户端证书指纹来实现几乎所需的行为:
SSL_CTX *ctx;
...
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
static inline int get_fingerprint(X509* cert, unsigned char *md, unsigned int *n)
{
return X509_digest(cert, EVP_sha1(), md, n);
}
static inline int compare_certificates(X509 *c1, X509 *c2)
{
unsigned char md1[EVP_MAX_MD_SIZE], md2[EVP_MAX_MD_SIZE];
unsigned int n1, n2;
if (!(get_fingerprint(c1, md1, &n1) && get_fingerprint(c2, md2, &n2))) {
return -1;
}
return memcmp(md1, md2, n1);
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
int err = X509_STORE_CTX_get_error(ctx);
/* Allow self-signed certificates */
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
if (0 != compare_certificates(ctx->current_cert, SSL_CTX_get0_certificate(ssl->ctx))) {
/* Peer certificate doesn't match the server certificate */
preverify_ok = 0;
}
/* More checks ... */
return preverify_ok;
}
因此,如果服务器和客户端证书指纹匹配,则验证通过。否则,连接将被服务器关闭。
我可能会在初始化阶段的某处计算可信证书的指纹,然后在 verify_callback
的循环中检查它们。但是,我不喜欢这个主意。应该有更简单的方法来做到这一点。
我以为 SSL_CTX_load_verify_locations()
正是我要找的(但看起来不是;我会解释原因):
SSL_CTX_load_verify_locations() specifies the locations for ctx, at which CA certificates for verification purposes are located. ... If CAfile is not NULL, it points to a file of CA certificates in PEM format. The file can contain several CA certificates... The certificates in CApath are only looked up when required, e.g. when building the certificate chain or when actually performing the verification of a peer certificate.
( man 3 SSL_CTX_load_verify_locations )
嗯,我想 SSL_VERIFY_FAIL_IF_NO_PEER_CERT
意味着验证对等证书。然后看起来我需要做的就是制作一堆受信任的证书并将其传递给 SSL_CTX_load_verify_locations()
:
bundle_file=CAbundle.pem
cd ./dir
rm -f $bundle_file
for i in *.pem; do
openssl x509 -in $i -text >> $bundle_file
done
c_rehash .
SSL_CTX *ctx;
const char *cafile = "dir/CAbundle.pem";
const char *capath = NULL;
...
if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
/* Unable to set verify locations ... */
}
cert_names = SSL_load_client_CA_file(cafile);
if (cert_names != NULL) {
SSL_CTX_set_client_CA_list(ctx, cert_names);
} else {
/* Handle error ... */
}
一切看起来都很好。但是服务器仍然接受具有不同对等证书的连接。
我在此处使用标准 OpenSSL 实用程序重现了此行为:https://gist.github.com/rosmanov/d960a5d58a96bdb730303c5b8e86f951
所以我的问题是:如何配置服务器以仅接受仅提供特定证书的对等点?
更新
我发现证书(CA bundle )的“白名单”确实有效,当我从 verify_callback
中删除以下内容时:
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
所以没有这个 block 一切正常。服务器响应与 CAbundle.pem
中列出的证书之一连接的客户端。如果客户端使用不同的证书连接,服务器将关闭连接。
但是,有一个奇怪的事情。在这两种情况下,openssl s_client
输出:
Verify return code: 18 (self signed certificate)
也许吧
if (!preverify_ok
&& err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
&& allow_self_signed
&& !cafile
&& !capath) {
preverify_ok = 1;
}
?
更新 2
现在我明白了为什么 openssl s_client
输出 Verify return code: 18 (self signed...)
。它不信任服务器的证书,除非 -CAfile
或 -CApath
包含服务器证书。并且服务器证书是自签名的。
最佳答案
解释(针对命令行)和半个答案(针对库):
我(这次完全)重做了你的要点,并被提醒这里有一个不一致的地方。命令行 openssl xxx
实用程序主要设计为测试/调试工具,特别是:
s_client
通常(匿名、SRP 等除外)从服务器接收证书链,但使用回调仅记录它获得的内容并忽略/覆盖任何错误;这是街区
depth=0 C = AU, ST = StateA, L = CityA, O = CompanyA, CN = localhost, emailAddress = a@gmail.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = AU, ST = StateA, L = CityA, O = CompanyA, CN = localhost, emailAddress = a@gmail.com
verify return:1
就在 s_client
输出中的 CONNECTED(fd)
之后,但如您所见,尽管出现错误,它仍继续握手,从而产生可用的连接。
s_server
比较复杂。默认情况下,它不会从客户端请求证书,只有当您指定 -verify
或 -Verify
(设置 SSL_VERIFY_PEER
时,这不是默认设置对于服务器),如果它确实请求证书客户端有选项是否发送一个(在 CertVerify 中有相关证明)。 如果客户端确实发送链,s_server
使用与 s_client
相同的回调,它会覆盖任何错误并继续连接;这在您的 s_server
输出中具有相同的 verify error:num-18...
这实际上意味着接收链中的“根(包括自签名,它是它自己的根)但不是在本地信任库中'。 如果客户端不发送链,-verify
继续,但-Verify
(也设置SSL_VERIFY_FAIL_IF_NO_PEER_CERT
) 使用警报 40 中止握手并返回错误,因此 s_server
输出非常不同:
verify depth is 0, must return a certificate Using default temp DH parameters Using default temp ECDH parameters ACCEPT ERROR 140679792887624:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate:s3_srvr.c:3271: shutting down SSL CONNECTION CLOSED ACCEPT
But a program using the library should work. I hacked up this simple test from parts of some other programs (hence the odd indentation):
/* SO36821430 2016-04-25 */#include <stdio.h>#if defined(_WIN32)&&!defined(WIN32)#define WIN32 /*anything*/#endif#ifdef WIN32 #include <winsock2.h> typedef int socklen_t; #define SOCKERR WSAGetLastError() #include "openssl/applink.c"#else #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #ifndef INADDR_NONE #define INADDR_NONE (ipaddr_t)-1 #endif typedef int SOCKET; enum { INVALID_SOCKET = -1, SOCKET_ERROR = -1 }; #define SOCKERR errno #define closesocket close#endif#include "openssl/ssl.h"#include "openssl/err.h"#include "openssl/rand.h"void sockerr (const char *what){ fprintf (stderr, "%s %d %s\n", what, SOCKERR, strerror(SOCKERR));}void sslerrn (const char *what){ fprintf (stderr, "* %s failed:\n", what); ERR_print_errors_fp (stderr);}void sslerr (const char *what, int rv){ fprintf (stderr, "* %s return %d:\n", what, rv); ERR_print_errors_fp (stderr);}void sslerrx (SSL * ssl, const char *what, int rv){ int rc = SSL_get_error (ssl, rv); if( rv == -1 && rc == SSL_ERROR_SYSCALL ) sockerr (what); else fprintf (stderr, "* %s return %d,%d\n", what, rv, rc); ERR_print_errors_fp (stderr); }void subj_oneline (X509 * cert, FILE *fp){ X509_NAME * subj = X509_get_subject_name (cert); BIO *bmem = BIO_new (BIO_s_mem()); char *ptr; int n; X509_NAME_print_ex (bmem, subj, 0, XN_FLAG_ONELINE); n = (int) BIO_get_mem_data (bmem, &ptr); if( n <= 0 ) ptr = "?", n = 1; fwrite (ptr,1,n,fp);}const char * inaddr;int inport;char buf [9999];int main (int argc, char* argv[] ){ int rv; struct sockaddr_in sin; socklen_t sinlen; SOCKET s1, s2; SSL_CTX *ctx = NULL; time_t now; struct tm * tm;#ifdef WIN32 struct WSAData wsa; rv = WSAStartup (MAKEWORD(1,1), &wsa); if(rv){ printf ("WSAStartup %d\n", rv); exit(1); }#endif if( argc < 2 || argc > 6 ) printf ("usage: %s port key cert CAcerts\n", argv[0]), exit(1); sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons (atoi(argv[1])); sin.sin_family = AF_INET; /**/ SSL_library_init(); SSL_load_error_strings(); ctx = SSL_CTX_new (SSLv23_server_method()); if( !ctx ){ sslerrn("CTX_new"); exit(1); } SSL_CTX_set_options (ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); rv = SSL_CTX_use_PrivateKey_file (ctx, argv[2], SSL_FILETYPE_PEM); if( rv != 1 ){ sslerr ("use_PrivateKey_file",rv); exit(1); } rv = SSL_CTX_use_certificate_file (ctx, argv[3], SSL_FILETYPE_PEM); if( rv != 1 ){ sslerr ("use_certificate_file",rv); exit(1); } SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); if( !SSL_CTX_load_verify_locations (ctx, argv[4], NULL) ){ sslerrn ("load_verify_locations"); exit(1); } SSL_CTX_set_client_CA_list (ctx, SSL_load_client_CA_file (argv[4])); /**/ if( (s1 = socket (AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET ){ sockerr ("socket()"); exit(1); } if( bind (s1, (struct sockaddr*)&sin, sizeof sin) < 0 ){ sockerr ("bind()"); exit(1); } if( listen (s1, 5) < 0 ){ sockerr ("listen()"); exit(1); } do{ sinlen = sizeof sin; if( (s2 = accept (s1, (struct sockaddr*)&sin, &sinlen)) == INVALID_SOCKET ){ sockerr ("accept()"); exit(1); } now = time(NULL); tm = localtime(&now); printf ("+ %s %u @%02d.%02d.%02d\n", inet_ntoa (sin.sin_addr), ntohs (sin.sin_port), tm->tm_hour, tm->tm_min, tm->tm_sec); /**/ SSL * ssl = SSL_new (ctx); if( !ssl ){ sslerrn("SSL_new"); goto next; } SSL_set_fd (ssl, s2); if( (rv = SSL_accept(ssl)) < 0 ){ sslerrx (ssl, "SSL_accept", rv); goto next; } { X509 * cert = SSL_get_peer_certificate (ssl); /*EVP_PKEY * key = cert? X509_get_pubkey (cert): NULL;*/ fprintf (stdout, "=%ld", SSL_get_verify_result (ssl)); if( cert ) putchar (':'), subj_oneline (cert, stdout); putchar ('\n'); } while( (rv = SSL_read (ssl, buf, sizeof buf)) > 0 ) printf ("%d: %.*s\n", rv, rv, buf); sslerrx (ssl, "SSL_read", rv);next: if( ssl ) SSL_free (ssl); /**/ now = time(NULL); tm = localtime(&now); printf ("- %s %u @%02d.%02d.%02d\n", inet_ntoa (sin.sin_addr), ntohs (sin.sin_port), tm->tm_hour, tm->tm_min, tm->tm_sec); closesocket (s2); } while (1); return 0;}
When run with $port cert1.key cert1.pem CAbundle.pem
and connected from client using cert2.key & cert2.pem this aborts the handshake with alert 48 unknown_ca and returns an error as desired:
+ 127.0.0.1 46765 @22.07.36
* SSL_accept return -1,1
140240689366696:error:14089086:SSL routines:ssl3_get_client_certificate:certificate verify failed:s3_srvr.c:3270:
- 127.0.0.1 46765 @22.07.36
HTH.
关于c - 相互身份验证并将用户证书限制为服务器上的特定集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36821430/
编辑:我似乎问错了这个问题。 我正在尝试寻找一种方法来查询一个集合是否在另一个集合中可用。例如: SELECT * FROM something WHERE (1, 3) IN (1, 2, 3, 4
这两种方法似乎 produce the same results ,但我一直很难真正说服人们第二种方法有效,因为它显然并不为人所知。 // Create some data var foo = { '
我一直在学习Kotlin,并且遇到过Collections API。在Kotlin之前,我一直在学习Java,并且我知道Java中有很多不同类型的Collections API。例如,我们使用List
为什么我会得到不同的行为: Collection col2 = new ArrayList(col); 集合 col2 = new ArrayList(); col2.addAll(col) 我正在与
所以我有一个代表专辑信息的 JSON 对象。给定“function updateRecords(id, prop, value)”我希望能够更新每个条目。正确的完成代码如下。 我得到了指示,粗体部分,
我想存储一个对象集合,这些对象根据它们所代表的值进行键控。这些键可以重复。例如: [4] => Bob [5] => Mary [5] => Sue [9] => Steve [10] =>
在检查 ArrayList API 时,我注意到一些看起来很奇怪的东西。 确实,这里是 ArrayList 构造函数实现,其中 Collection 作为参数传递: public ArrayList(
我正在为 API 编写一个 swagger 定义文件。 API 是用于 GET 请求的 /path/to/my/api: get: summary: My Custom API d
我知道scala.collection包中有两个非常有用的对象,可以帮助我们实现这个目标: JavaConverters(如果我想明确说明并准确说明我要转换的内容) JavaConversions(如
我已经阅读了无数其他帖子,但似乎无法弄清楚发生了什么,所以是时候寻求帮助了。 我正在尝试将包含集合的域实体映射到也包含集合的 dtos。 这是一个原始示例; (我提前为代码墙道歉,我尽量保持简短):
我正在创建一个具有 ArrayList 的类,因此当我调用构造函数时,它会初始化该数组: public class ElementsList { private ArrayList list;
我正在阅读事件指南和指南的开头,它说: You can also add an event listener to any element in the this.$ collection using
我是 Python 新手,想知道如何使用键在字典中存储不同数据类型的列表 例如 - {[Key1,int1,int1,String1] , [Key2,int2,int2,String2], [Key
int[] mylist = { 2, 4, 5 }; IEnumerable list1 = mylist; list1.ToList().Add(1); // why 1 does not get
我在 UI 表单中的每一行之后将以下内容添加到 HashMap 集合中 声明 Map> map = new HashMap>(); List valSetOne = new ArrayList();
我正在开发我的第一个 Java 项目,我有一个问题。问题应该很简单(虽然代码不是那么短,但没有理由被吓倒:))。我创建了一个基本的角色扮演游戏,并且有一个定义每个角色的抽象类“Character”。在
我正在开发一款应用程序,可以为用户收集推文、Facebook 状态和 Facebook 照片。目前,用户确切地设定了他们希望这种收获发生的时间和时间,并且蜘蛛会在此期间拉取数据。 when 和 to
有谁知道在 C# 中是否有与 Java 的 Set 集合等效的好方法?我知道您可以通过填充但忽略值来使用 Dictionary 或 HashTable 在某种程度上模仿集合,但这不是一种非常优雅的方式
EXISTS 该函数返回 集合中第一个元素的索引,如果集合为空,返回NULLNULLNULL Collecti
RDF集合是通过属性 rdf:parseType="Collection" 来描述仅包含指定成员的组 rdf:parseType="Collection" 属
我是一名优秀的程序员,十分优秀!