proxy-certificates
名稱
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
- text
-
表示字串為位元組序列,沒有任何編碼
policy=text:räksmörgås
- hex
-
表示字串為編碼十六進位編碼的二進位資料,每個位元組之間(每兩個十六進位數字)都有冒號
policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73
- file
-
表示應從檔案取得政策文字。字串即為檔案名稱。這對於超過數行的政策(例如 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 The OpenSSL Project Authors。保留所有權利。
根據 Apache 授權 2.0(「授權」)授權。您不得在不遵守授權的情況下使用此檔案。您可以在原始程式碼散佈中的 LICENSE 檔案中取得一份副本,或前往 https://www.openssl.org/source/license.html。