Certificate chain validation for OpenSSL

Source: Internet
Author: User
Tags goto openssl

Use the OpenSSL to verify the certificate chain with the following command:
Debian:/home/zhaoya/openssl#openssl verify-cafile Root_cert User_cert
The Root_cert can contain a lot of certificates, you can use the Cat command to merge multilevel CA certificates into a file, and then the program will load after startup Root_cert,root_cert will be in memory to form a stack structure, the order of each certificate and file inside the same. However, if there are several certificates in the file with the same name, the use of the above command will cause problems, in order to correct this is not a problem, or check the code. Why is this not a problem, because the CA does not advocate the same name, or even if it can have the same name, it can be separated by other fields. In any case, it's not a bad thing to look up code, at least a new understanding of OpenSSL. Here is the messy certificate chain Architecture (), which encloses the name of the certificate CN entry and [] represents the issuing relationship:
ROOT1 (Cn=root) [Subca1_1 (Cn=subca1_1) [User1_1 (Cn=user1_1)],subca1_2 (cn=subca1_1) [User1_2 (cn=user1_2)],SubCA1_3 ( cn=subca1_3) [User1_3 (Cn=user1_3)]]
ROOT2 (Cn=root) [Subca2_1 (Cn=subca1_1) [User2_1 (Cn=user2_1)],subca2_2 (cn=subca2_2) [User2_2 (Cn=user2_2)]]
ROOT3 (CN=ROOT3) [Subca3_1 (Cn=subca3_1) [User3_1 (Cn=user3_1)]]

#ifdef Zhaoya
#define MAX_DEPTH 6
struct List_entity {
int num;
struct list_x *first[max_depth];
}
struct List_xit {
void * PTR;
struct List_xit *prev;
struct List_xit *next;
}
Note 0 (Doing this line and doing the bottom will understand why it starts at 0 instead of 1, just as someone is not 10 after 9, but a)
typedef struct LIST_ENTITY list_e;
typedef struct LIST_XIT list_x;
int Get_isuser_all (X509 **issuer, X509_store_ctx *ctx, X509 *x, List_e * le)
{
X509_name *xn;
X509_object obj, *pobj;
int I, OK, idx;
Xn=x509_get_issuer_name (x); Get the name of the issuer
IDX = X509_object_idx_by_subject (Ctx->ctx->objs, x509_lu_x509, xn);//Note 4
X509 *xyz = NULL;
for (i = idx i < Sk_x509_object_num (CTX-&GT;CTX-&GT;OBJS); i++)
{
Pobj = Sk_x509_object_value (Ctx->ctx->objs, i);
if (ctx->check_issued (CTX, X, pobj->data.x509))
{//Get CA Certificate of all names in the same level CA, add to list in sequence
XYZ = pobj->data.x509;
list_x * lx = (list_x*) calloc (sizeof (list_x));
Lx->ptr = pobj->data.x509;
list_x * Header = le->first[le->num];
Lx->next = header;
Le->first[le->num] = lx;
}
}
if (XYZ) {
Le->num + +;
*issuer = xyz;
return 1;
}
return 0;
}
int Vfy (X509_store_ctx *ctx, X509 * XI, X509 * xs)
{
Evp_pkey *pkey=null;
int OK;
Int (*CB) (int xok,x509_store_ctx *xctx); Note 3
cb=ctx->verify_cb;
if (xs->valid) goto last;
if ((Pkey=x509_get_pubkey (xi)) = = NULL) {
ctx->error=x509_v_err_unable_to_decode_issuer_public_key;
ctx->current_cert=xi;
Ok= (*CB) (0,CTX);
if (!ok)
return-1;
else if (x509_verify (Xs,pkey) <= 0) {
ctx->error=x509_v_err_cert_signature_failure;
ctx->current_cert=xs;
Evp_pkey_free (Pkey);
Ok= (*CB) (0,CTX);
if (!ok) {
return-1;
}
}
Xs->valid = 1;
Last
OK = check_cert_time (CTX, XS);
if (!ok)
return-1;
ctx->current_issuer=xi;
ctx->current_cert=xs;
OK = (*CB) (1,CTX);
Evp_pkey_free (Pkey);
Return OK? 1:-1;
}
int all_cert_verify (X509_store_ctx *ctx, list_e * le)
{
int ok=0,;
X509 *xs,*xi;
ctx->error_depth=n-1;
list_x * lx = le->first[0];
xs = lx->ptr;
int CO = 1, res;
for (; Co < le->num; co++) {
ctx->error_depth = CO;
list_x *LX = le->first[co];
while (LX) {
Xi = lx->ptr;
res = Vfy (CTX, Xi, XS);
if (res ==1) {
xs = XI;
Break
else if (res = = 1 && ctx->error!= x509_v_err_cert_signature_failure)
return 0; Note If the error is caused by a non-signature error, exit directly
LX = lx->next;
}
if (lx = NULL) {//If the same layer does not have any certificate to verify the underlying certificate, then exit directly, based on the certificate chain is a single path
OK = 0;
Break
}
}
if (ctx->check_issued (CTX, Xi, xi) && co = = Le->num) {//finally verify the root certificate, which is a self-signed certificate
OK = Vfy (CTX, Xi, xi);
}
return OK;
}
#endif

int X509_verify_cert (X509_store_ctx *ctx)
{
X509 *x,*xtmp,*chain_ss=null;
X509_name *xn;
int bad_chain = 0;
X509_verify_param *param = ctx->param;
int depth,i,ok=0;
int num;
Int (*CB) (int xok,x509_store_ctx *xctx);
Stack_of (X509) *sktmp=null;
#ifdef Zhaoya
List_e * le = NULL;
#endif
cb=ctx->verify_cb;
if (Ctx->chain = = NULL) {
Ctx->chain=sk_x509_new_null ());
Sk_x509_push (Ctx->chain,ctx->cert);
#ifdef Zhaoya
Le = (list_e*) calloc (sizeof (list_e));
Le->num = 0;
list_x * lx = (list_x*) malloc (sizeof (list_x));
memset (LX, 0, sizeof (list_x));
Le->first[le->num] = lx;
Lx->ptr = ctx->cert;
Lx->next =null;
Le->num + +;
#endif
Crypto_add (&ctx->cert->references,1,crypto_lock_x509);
ctx->last_untrusted=1;
}
if (ctx->untrusted!= NULL
&& (Sktmp=sk_x509_dup (ctx->untrusted)) = = NULL) {
...
}
Num=sk_x509_num (Ctx->chain);
X=sk_x509_value (ctx->chain,num-1);
depth=param->depth;
for (;;) {//NOTE 1
if (depth < num) break;
Xn=x509_get_issuer_name (x);
if (ctx->check_issued (CTX, x,x)) break;
if (ctx->untrusted!= NULL) {
Xtmp=find_issuer (CTX, sktmp,x);
if (xtmp!= NULL) {
if (!sk_x509_push (ctx->chain,xtmp)) {...
}
Crypto_add (&xtmp->references,1,crypto_lock_x509);
Sk_x509_delete_ptr (SKTMP,XTMP);
ctx->last_untrusted++;
x=xtmp;
num++;
Continue
}
}
Break
}
I=sk_x509_num (Ctx->chain); Get the number of certificates in the current constructed certificate chain
X=sk_x509_value (ctx->chain,i-1); Get the top certificate of the currently constructed certificate chain
xn = x509_get_subject_name (x);
if (ctx->check_issued (CTX, x, x)) {
if (Sk_x509_num (ctx->chain) = = 1) {
The top certificate is a self-signed condition, which does not have to continue looking for its issuer in the CA collection because it is not possible to find it and it is self-signed. But the code has to go to the internal_verify below.
}
}
The following for loop finds a certificate in the collection of certificates according to the IsUser field in the order from bottom to top
int CO = 0;
for (;;) {
CO + + 1;
if (depth < num) break;
if (ctx->check_issued (ctx,x,x))
Break
#ifdef Zhaoya
OK = Get_isuser_all (&xtmp, CTX, X, le); Get all the certificates on the same level, not just get one and return.
#else
OK = Ctx->get_issuer (&xtmp, CTX, x); Just get a "plausible" certificate and return it, but actually it's just based on

CN name Lookup Superior CA Certificate
#endif
if (OK < 0) return OK;
if (ok = = 0) break;
x = xtmp; The layers are looked up until a self-signed root.
if (!sk_x509_push (ctx->chain,x)) {
X509_free (XTMP);
X509err (x509_f_x509_verify_cert,err_r_malloc_failure);
return 0;
}
num++;
}
Xn=x509_get_issuer_name (x);
if (!ctx->check_issued (ctx,x,x)) {//See if the top-level certificate is a self-signed certificate
if ((Chain_ss = NULL) | |!ctx->check_issued (CTX, X, CHAIN_SS)) {
if (ctx->last_untrusted >= num)
ctx->error=x509_v_err_unable_to_get_issuer_cert_locally;
Else
ctx->error=x509_v_err_unable_to_get_issuer_cert;
ctx->current_cert=x;
} else {
Sk_x509_push (CTX-&GT;CHAIN,CHAIN_SS);
num++;
ctx->last_untrusted=num;
ctx->current_cert=chain_ss;
ctx->error=x509_v_err_self_signed_cert_in_chain;
Chain_ss=null;
}
ctx->error_depth=num-1;
Bad_chain = 1;
OK=CB (0,CTX);
if (!ok) goto end;
}
#ifndef Zhaoya//NOTE 2
if (ctx->verify!= NULL) {
Ok=ctx->verify (CTX);
Else
Ok=internal_verify (CTX);
#else
OK = all_cert_verify (CTX, le);
#endif
if (!ok) goto end;
End
#ifdef Zhaoya
int count = 0;
for (; count < le->num; count++) {
list_x *x = le->first[count];
while (x) {
Free (x);
x = x->next;
}
}
Free (LE);
#endif
return OK;
}
Note 0: The extended data structure of the certificate chain, designed in a very rough way, is better designed to use the Hlist_head embedded list in the Linux kernel, hlist instead of List_head because the former is a hanging chain structure, And from the local memory or disk loaded certificate logically is also a chain structure, if the name of the certificate is 1,2,3, the number of certificates of these three names is 2,3,1, then in-memory certificates can be arranged in the following order: 112223, can also 123221, ... Although the physical is very disorderly, but the logic is hanging chain, the main chain has 3 elements, respectively, representing the 3 name of the certificate, hanging chain is the name of duplicate certificate, similar to hash conflict linked list, so the certificate chain can be used hlist way to organize.
Note 1: Use the incoming certificate chain as a priority, so you can prioritize the certificates in the blacklist. In fact, the most untrusted certificate is the highest priority, generally speaking, the untrusted in the list here is not a local certificate, and most of the certificate from the remote, such as SSL server Authentication client certificate, but the client certificate is generally n-level CA issued, So if the server side does not have a CA certificate from the client certificate issuer, then the validation will fail, so the client not only passes its own client certificate to the server, but also the issuer CA certificate of its certificate passes through, obviously, this n-level CA's certificate is not trusted to the server, Priority to use this certificate can give priority to rejecting malicious attacks, the relationship here is a bit tricky, if the server has this n-level CA certificate, and this client just want to prank and modify this N-level certificate, and then with their own client certificate sent to the server, Then the server will discover this prank and let the client pay the price. The use of this untrusted chain is a little more normal to exclude the blacklist, or the example above, if the issuer CA for a client certificate is blacklisted for some reason, this matter does not notify the client, the client does not actively query, and the server knows this thing, Then there will be a blacklist list on the server side, the CA certificate of the client certificate issuer will be in it, and when the client certificate is validated, the server-side certificate chain loading Order is blacklisted, and the CA certificate from the client is superior to the CA certificate from the client, which is superior to the local trusted certificate of the server. So the client section CA certificate in the blacklist of this matter will be first detected. Above just two examples, the nature of the untrusted linked list is similar to that of ACLs, which match rejected and matched accepted.

Note 2: This is the general validation method, if not because the structure and function parameters can not be modified, it is entirely possible to do not use macros for branch management, this is actually to ensure binary compatibility and step back, list_e pointers can be put into the X509_STORE_CTX structure, so , you can implement a verify callback function to manage, you do not have to precompile macros to judge. All_cert_verify implementation of the bottom-up verification logic, and Internal_verify is a single list of the Top-down verification, note that only a single certificate chain, which may cause some certificates in the presence of the same name CA will always lose the chance of being validated, In fact, it can be passed by a certain authentication in the same CA, whether it can be validated by a CA with the same name depends on the order in which the CAs are loaded with the same name, a solution is to traverse all the loaded certificates when the actual validation is done, so there is no operation of the construction chain, which I have not implemented. In that case, the code will be much less, of course, the time complexity of the traversal will also increase, the algorithm implementation will be given later, a more reasonable solution is I write all_cert_verify, you can see inside actually did not use to ctx->chain this stack, This is written to make less changes to the original code, otherwise, directly delete all the code about Ctx->chain. All_cert_verify implementation is still very simple, I believe that there are a lot of abnormal situation did not deal with, given only the idea of the algorithm. In addition, the construction of List_e also did not deal with the untrusted linked list, but I believe that the addition is very simple.
Note 3: If the signature verification fails, then the CTX VERIFY_CB function is invoked, and if the provider of the callback function does not care about the error, then the error may be cleared and then the validation continues, whereas if the signature verification succeeds, The provider of the callback function may also terminate the validation with its own policy, which is also reflected in the callback function. The first parameter of the callback function is the most recent result value, in which you can set the error value of the CTX and whether you can ignore the error, if you can ignore it, then empty the CTX error and then the callback function returns 1, of course, according to CTX's current settings, When the first parameter is 1, set the error of the CTX, and then return 0 to terminate the verification operation. The
annotation 4:x509_object_idx_by_subject This function makes a small optimization so that the traversal does not start from the beginning, but from the certificate that matches the first name.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.