xmlsec library - obtain subject of signing key

1.2k Views Asked by At

I am using the xmlsec library to verify the signature of an SAML assertion. My code is almost identical to the verify4.c example provided on the xmlsec web page.

I am linking against the xmlsec-openssl lib, so using openssl as the crypto engine.

I was expecting that xmlsec would consider the signature valid only if it was signed with one of the specific certificate(s) I had loaded into the key manager.

However, the signature seems to be considered valid if signed with ANY certificate that can be verified by openssl. This means that someone could forge an SAML response just by buying a certificate from a trusted root CA and using that to sign whatever response they want.

Not only that, but the xmlsec1 command line tool provided with the library seems to do the same thing:

xmlsec1 --verify --dtd-file saml.dtd --pubkey-cert-pem my_cert.cer sample_saml_assertion.xml

...
OK
SignedInfo References (ok/all): 1/1
Manifests References (ok/all): 0/0

Actually, in an ideal world, I would be happy for any valid signing key to be used as long as I could identify the subject of the key and therefore confirm it was signed by the entity I expected. That would simplify matters when the sender of the SAML responses changed their signing key. But I have not been able to find a simple way to extract the details of the cert that was used to verify the signature.

Failing that, can I make it accept only the certificate(s) I have specified when verifying the signature?

1

There are 1 best solutions below

0
On BEST ANSWER

While writing the question I realized I had not tried the --print-debug option for xmlsec1. When I tried that I found that it does indeed print the subject and issuer of the cert that was used to verify the signature.

That led me to realize that the information must be present, so it is a question of how to access it. Tracing through the code, I was able to write this little snippet which does the trick:

/* If signature is valid, then the list dsigCtx->signKey contains
   the signing key, data dsigCtx->signKey->dataList contains the certificate */
xmlSecPtrListPtr keyDataList = dsigCtx->signKey->dataList;

/* Iterate through the data list to find the X509 cert */
xmlSecSize n = xmlSecPtrListGetSize(keyDataList);
xmlSecSize i;
for (i=0; i<n; i++) {
    xmlSecKeyDataPtr item = xmlSecPtrListGetItem(keyDataList, i);
    if (xmlSecKeyDataIsValid(item) && xmlSecKeyDataCheckId(item, xmlSecOpenSSLKeyDataX509Id)) {

        /* Extract openssl cert */
        X509* cert = xmlSecOpenSSLKeyDataX509GetKeyCert(item);
        char cn_buff[256];
        if(cert != NULL) {
            /* Get the CN */
            X509_NAME * subject_name = X509_get_subject_name(cert);
            int nid_cn = OBJ_txt2nid("CN");
            X509_NAME_get_text_by_NID(subject_name, nid_cn, cn_buff, 255);

            /* Here you would compare it to the expected certificate */
            fprintf(stdout, "CN=%s\n", cn_buff);
        } else {
            fprintf(stdout, "Failed to obtain signing key cert\n");
        }
    }
}

This seems a very complex way to obtain something so fundamental, so I am sure there must be an easier way.