代理证书
名称
proxy-certificates - OpenSSL中的代理证书
描述
代理证书在RFC 3820中定义。它们用于将权限扩展到其他实体(通常是计算机进程,有时也可能是用户本身)。这允许实体代表EE(终端实体)证书所有者执行操作。
有效代理证书的要求是
它们由终端实体颁发,可以是普通EE证书,也可以是另一个代理证书。
它们不得具有subjectAltName或issuerAltName扩展。
它们必须具有proxyCertInfo扩展。
它们必须具有其发行者的主题,并添加一个commonName。
启用代理证书验证
OpenSSL期望想要使用代理证书的应用程序能够专门识别它们,并明确表示。这是通过设置X509验证标志来完成的
X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
或
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_ALLOW_PROXY_CERTS);
有关此要求的讨论,请参阅"注释"。
创建代理证书
可以使用openssl-x509(1)命令和一些额外的扩展来创建代理证书
[ proxy ]
# A proxy certificate MUST NEVER be a CA certificate.
basicConstraints = CA:FALSE
# Usual authority key ID
authorityKeyIdentifier = keyid,issuer:always
# The extension which marks this certificate as a proxy
proxyCertInfo = critical,language:id-ppl-anyLanguage,pathlen:1,policy:text:AB
也可以在单独的部分中指定代理扩展
proxyCertInfo = critical,@proxy_ext
[ proxy_ext ]
language = id-ppl-anyLanguage
pathlen = 0
policy = text:BC
策略值具有特定的语法,syntag:string,其中syntag确定将对字符串执行的操作。识别以下syntag
- 文本
-
指示字符串是字节序列,没有任何编码
policy=text:räksmörgås
- 十六进制
-
指示字符串是十六进制编码的二进制数据,字节之间用冒号分隔(每两位十六进制数字)
policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73
- 文件
-
指示应从文件中获取策略的文本。然后字符串是文件名。这对于超过几行的策略(例如XML或其他标记)很有用。
请注意,代理策略值决定了在代理证书期间授予进程的权限,应用程序负责解释和组合这些策略。>
使用代理扩展,创建代理证书只需两个命令
openssl req -new -config proxy.cnf \
-out proxy.req -keyout proxy.key \
-subj "/DC=org/DC=openssl/DC=users/CN=proxy"
openssl x509 -req -CAcreateserial -in proxy.req -out proxy.crt \
-CA user.crt -CAkey user.key -days 7 \
-extfile proxy.cnf -extensions proxy
您还可以使用另一个代理证书作为发行者来创建代理证书。请注意,此示例对代理扩展使用不同的配置部分
openssl req -new -config proxy.cnf \
-out proxy2.req -keyout proxy2.key \
-subj "/DC=org/DC=openssl/DC=users/CN=proxy/CN=proxy 2"
openssl x509 -req -CAcreateserial -in proxy2.req -out proxy2.crt \
-CA proxy.crt -CAkey proxy.key -days 7 \
-extfile proxy.cnf -extensions proxy_2
在应用程序中使用代理证书
为了解释代理策略,应用程序通常会从一些默认权限(可能根本没有权限)开始,然后通过根据代理证书链、用户证书和CA证书检查权限来计算结果权限。
复杂的部分是如何在应用程序和证书验证过程之间传递数据。
此类处理需要以下要素
一个回调函数,将在验证每个证书时调用。回调函数为每个证书调用多次,因此必须小心地在正确的时间进行代理策略解释。还需要在检查EE证书时填写默认值。
一个在应用程序代码和回调函数之间共享的数据结构。
一个设置所有内容的包装函数。
一个ex_data索引函数,用于创建指向附加到X509验证上下文的通用ex_data存储的索引。
以下骨架代码可以用作起点
#include <string.h>
#include <netdb.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#define total_rights 25
/*
* In this example, I will use a view of granted rights as a bit
* array, one bit for each possible right.
*/
typedef struct your_rights {
unsigned char rights[(total_rights + 7) / 8];
} YOUR_RIGHTS;
/*
* The following procedure will create an index for the ex_data
* store in the X509 validation context the first time it's
* called. Subsequent calls will return the same index.
*/
static int get_proxy_auth_ex_data_idx(X509_STORE_CTX *ctx)
{
static volatile int idx = -1;
if (idx < 0) {
X509_STORE_lock(X509_STORE_CTX_get0_store(ctx));
if (idx < 0) {
idx = X509_STORE_CTX_get_ex_new_index(0,
"for verify callback",
NULL,NULL,NULL);
}
X509_STORE_unlock(X509_STORE_CTX_get0_store(ctx));
}
return idx;
}
/* Callback to be given to the X509 validation procedure. */
static int verify_callback(int ok, X509_STORE_CTX *ctx)
{
if (ok == 1) {
/*
* It's REALLY important you keep the proxy policy check
* within this section. It's important to know that when
* ok is 1, the certificates are checked from top to
* bottom. You get the CA root first, followed by the
* possible chain of intermediate CAs, followed by the EE
* certificate, followed by the possible proxy
* certificates.
*/
X509 *xs = X509_STORE_CTX_get_current_cert(ctx);
if (X509_get_extension_flags(xs) & EXFLAG_PROXY) {
YOUR_RIGHTS *rights =
(YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx,
get_proxy_auth_ex_data_idx(ctx));
PROXY_CERT_INFO_EXTENSION *pci =
X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL);
switch (OBJ_obj2nid(pci->proxyPolicy->policyLanguage)) {
case NID_Independent:
/*
* Do whatever you need to grant explicit rights
* to this particular proxy certificate, usually
* by pulling them from some database. If there
* are none to be found, clear all rights (making
* this and any subsequent proxy certificate void
* of any rights).
*/
memset(rights->rights, 0, sizeof(rights->rights));
break;
case NID_id_ppl_inheritAll:
/*
* This is basically a NOP, we simply let the
* current rights stand as they are.
*/
break;
default:
/*
* This is usually the most complex section of
* code. You really do whatever you want as long
* as you follow RFC 3820. In the example we use
* here, the simplest thing to do is to build
* another, temporary bit array and fill it with
* the rights granted by the current proxy
* certificate, then use it as a mask on the
* accumulated rights bit array, and voilà, you
* now have a new accumulated rights bit array.
*/
{
int i;
YOUR_RIGHTS tmp_rights;
memset(tmp_rights.rights, 0,
sizeof(tmp_rights.rights));
/*
* process_rights() is supposed to be a
* procedure that takes a string and its
* length, interprets it and sets the bits
* in the YOUR_RIGHTS pointed at by the
* third argument.
*/
process_rights((char *) pci->proxyPolicy->policy->data,
pci->proxyPolicy->policy->length,
&tmp_rights);
for(i = 0; i < total_rights / 8; i++)
rights->rights[i] &= tmp_rights.rights[i];
}
break;
}
PROXY_CERT_INFO_EXTENSION_free(pci);
} else if (!(X509_get_extension_flags(xs) & EXFLAG_CA)) {
/* We have an EE certificate, let's use it to set default! */
YOUR_RIGHTS *rights =
(YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx,
get_proxy_auth_ex_data_idx(ctx));
/*
* The following procedure finds out what rights the
* owner of the current certificate has, and sets them
* in the YOUR_RIGHTS structure pointed at by the
* second argument.
*/
set_default_rights(xs, rights);
}
}
return ok;
}
static int my_X509_verify_cert(X509_STORE_CTX *ctx,
YOUR_RIGHTS *needed_rights)
{
int ok;
int (*save_verify_cb)(int ok,X509_STORE_CTX *ctx) =
X509_STORE_CTX_get_verify_cb(ctx);
YOUR_RIGHTS rights;
X509_STORE_CTX_set_verify_cb(ctx, verify_callback);
X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(ctx),
&rights);
X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
ok = X509_verify_cert(ctx);
if (ok == 1) {
ok = check_needed_rights(rights, needed_rights);
}
X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb);
return ok;
}
如果使用SSL或TLS,则可以使用上述代码轻松设置回调以正确检查证书
SSL_CTX_set_cert_verify_callback(s_ctx, my_X509_verify_cert,
&needed_rights);
注释
迄今为止,似乎代理证书仅在了解它们的系统环境中使用,并且似乎没有人调查如何在这样的环境之外使用或滥用它们。
因此,OpenSSL要求了解代理证书的应用程序也必须明确表示这一点。
subjectAltName和issuerAltName在代理证书中是被禁止的,并且在OpenSSL中强制执行。主题必须与发行者相同,并添加一个commonName。
另请参阅
X509_STORE_CTX_set_flags(3),X509_STORE_CTX_set_verify_cb(3),X509_VERIFY_PARAM_set_flags(3),SSL_CTX_set_cert_verify_callback(3),openssl-req(1),openssl-x509(1),RFC 3820
版权
版权所有 2019-2020 OpenSSL项目作者。保留所有权利。
根据Apache许可证2.0(“许可证”)获得许可。除非符合许可证,否则您不得使用此文件。您可以在源代码分发中的LICENSE文件中或https://www.openssl.org/source/license.html中获取副本。