Skip to content

Tutorial Verify And Decrypt

Aleksey Sanin edited this page Oct 12, 2026 · 5 revisions

Verifying and decrypting documents

Overview

The XML Security Library verifies signatures or decrypts data by processing the signed/encrypted XML document, finding the required key(s) in the keys manager or in the signature/encryption context, performing the necessary cryptographic operations, and returning the verification results or the decrypted data.

Figure: The verification or decryption processing model

The verification or decryption processing model

Verifying a signed document

A typical signature verification process includes the following steps:

  • Load the signed XML file and select the dsig:Signature node.
  • Create a signature context using the xmlSecDSigCtxCreate function.
  • Load the verification key(s), X509 certificates, etc. into the keys manager or set the key in the signature context (the signKey member of the xmlSecDSigCtx structure).
  • Verify the signature by calling the xmlSecDSigCtxVerify function.
  • Check the returned value and the verification status (the status member of the xmlSecDSigCtx structure).
  • Perform additional checks to ensure the signature is valid (for example, validate References and Transforms to ensure that the expected data was actually signed). See XML Signature Best Practices for more details.
  • Destroy the signature context using the xmlSecDSigCtxDestroy function.

Example: Verifying a document

/**
 * @brief Verifies XML signature in a file using a public key.
 * @details Verifies XML signature in #xml_file using public key from #key_file.
 * @param xml_file the signed XML file name.
 * @param key_file the PEM public key file name.
 * @param key_name the name to assign to the key (used to match the signature KeyName).
 * @return 0 on success or a negative value if an error occurs.
 */
int
verify_file(const char* xml_file, const char* key_file, const char* key_name) {
    xmlDocPtr doc = NULL;
    xmlNodePtr node = NULL;
    xmlSecDSigCtxPtr dsigCtx = NULL;
    int res = -1;

    assert(xml_file);
    assert(key_file);
    assert(key_name);

    /* load file: LIBXML_VERSION encodes major*10000+minor*100+micro,
     * so 21300 is libxml2 2.13.0, where XML_PARSE_NO_XXE was introduced. */
#if LIBXML_VERSION >= 21300
    doc = xmlReadFile(xml_file, NULL, XML_PARSE_PEDANTIC | XML_PARSE_NONET | XML_PARSE_NOENT | XML_PARSE_NO_XXE);
#else /* LIBXML_VERSION >= 21300 */
    doc = xmlReadFile(xml_file, NULL, XML_PARSE_PEDANTIC | XML_PARSE_NONET | XML_PARSE_NOENT);
#endif /* LIBXML_VERSION >= 21300 */
    if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
        fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_file);
        goto done;
    }

    /* find start node */
    node = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs);
    if(node == NULL) {
        fprintf(stderr, "Error: start node not found in \"%s\"\n", xml_file);
        goto done;
    }

    /* create signature context, we don't need keys manager in this example */
    dsigCtx = xmlSecDSigCtxCreate(NULL);
    if(dsigCtx == NULL) {
        fprintf(stderr,"Error: failed to create signature context\n");
        goto done;
    }

    /* load public or private key */
    dsigCtx->signKey = xmlSecCryptoAppKeyLoadEx(key_file, xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
    if(dsigCtx->signKey == NULL) {
        fprintf(stderr,"Error: failed to load public pem key from \"%s\"\n", key_file);
        goto done;
    }

    /* set the key name */
    if(xmlSecKeySetName(dsigCtx->signKey, BAD_CAST key_name) < 0) {
        fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", key_file);
        goto done;
    }

    /* Verify signature */
    if(xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
        fprintf(stderr,"Error: signature verification failed\n");
        goto done;
    }

    /* verify results and print outcome to stdout */
    if(verify_signature_results(dsigCtx) == 0) {
        fprintf(stdout, "Signature is OK\n");
    } else {
        fprintf(stdout, "Signature is INVALID\n");
        goto done;
    }

    /* success */
    res = 0;

done:
    /* cleanup */
    if(dsigCtx != NULL) {
        xmlSecDSigCtxDestroy(dsigCtx);
    }

    if(doc != NULL) {
        xmlFreeDoc(doc);
    }
    return(res);
}

/**
 * @brief Verifies XML signature results match expected data.
 * @details Verifies XML signature results to ensure that signature was applied
 * to the expected data.
 * @param dsigCtx the XMLDSig context
 * @return 0 on success or a negative value if an error occurs.
 */
int
verify_signature_results(xmlSecDSigCtxPtr dsigCtx) {
    xmlSecDSigReferenceCtxPtr dsigRefCtx;
    xmlSecTransformPtr transform;

    assert(dsigCtx);

    /* check that signature verification succeeded */
    if(dsigCtx->status != xmlSecDSigStatusSucceeded) {
        fprintf(stderr,"Error: Signature verification result is not SUCCESS\n");
        return(-1);
    }

    /* in this example we expect exactly ONE reference with URI="" and
    *  exactly ONE enveloped signature transform (i.e. the whole document is signed)*/
    if(xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) != 1) {
        fprintf(stderr,"Error: Exactly one Reference is expected\n");
        return(-1);
    }
    dsigRefCtx = (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), 0);
    if((dsigRefCtx == NULL) || (dsigRefCtx->status != xmlSecDSigStatusSucceeded)) {
        fprintf(stderr,"Error: Reference verification result is not SUCCESS\n");
        return(-1);
    }

    /* check URI: a NULL URI (Reference without a URI attribute) means the whole document,
     * which xmlsec treats the same as an empty URI */
    if((dsigRefCtx->uri != NULL) && (!xmlStrEqual(dsigRefCtx->uri, BAD_CAST ""))) {
        fprintf(stderr,"Error: Reference URI value doesn't match expected one\n");
        return(-1);
    }

    /* check transforms: we expect only one "enveloped signature" transform */
    transform = dsigRefCtx->transformCtx.first;
    if((transform == NULL) || (!xmlStrEqual(transform->id->name, xmlSecNameEnveloped))) {
        fprintf(stderr,"Error: First Transform name '%s' doesn't match expected '%s'\n", (transform != NULL ? transform->id->name : BAD_CAST "NULL"), xmlSecNameEnveloped);
        return(-1);
    }

    /* all other transforms should be inserted by XMLSec */
    transform = transform->next;
    while(transform != NULL) {
        if((transform->flags & XMLSEC_TRANSFORM_FLAGS_USER_SPECIFIED) != 0) {
            fprintf(stderr,"Error: Found unexpected Transform name '%s'\n", transform->id->name);
            return(-1);
        }
        transform = transform->next;
    }

    /* all good! */
    return(0);
}

Full Program Listing

Decrypting an encrypted document

A typical decryption process includes the following steps:

  • Load the encrypted XML file and select the enc:EncryptedData node.
  • Create an encryption context using the xmlSecEncCtxCreate function.
  • Load the decryption key(s), X509 certificates, etc. into the keys manager or set the key in the encryption context (the encKey member of the xmlSecEncCtx structure).
  • Decrypt the data by calling the xmlSecEncCtxDecrypt function.
  • Check the returned value to make sure there are no errors.
  • Destroy the encryption context using the xmlSecEncCtxDestroy function.

Example: Decrypting a document

/**
 * @brief Decrypts an encrypted XML file using a Triple DES key.
 * @details Decrypts the XML file #enc_file using Triple DES key from #key_file and
 * prints results to stdout.
 * @param enc_file the encrypted XML file name.
 * @param key_file the Triple DES key file.
 * @return 0 on success or a negative value if an error occurs.
 */
int
decrypt_file(const char* enc_file, const char* key_file) {
    xmlDocPtr doc = NULL;
    xmlNodePtr node = NULL;
    xmlSecEncCtxPtr encCtx = NULL;
    int res = -1;

    assert(enc_file);
    assert(key_file);

    /* load template: LIBXML_VERSION encodes major*10000+minor*100+micro,
     * so 21300 is libxml2 2.13.0, where XML_PARSE_NO_XXE was introduced. */
#if LIBXML_VERSION >= 21300
    doc = xmlReadFile(enc_file, NULL, XML_PARSE_PEDANTIC | XML_PARSE_NONET | XML_PARSE_NOENT | XML_PARSE_NO_XXE);
#else /* LIBXML_VERSION >= 21300 */
    doc = xmlReadFile(enc_file, NULL, XML_PARSE_PEDANTIC | XML_PARSE_NONET | XML_PARSE_NOENT);
#endif /* LIBXML_VERSION >= 21300 */
    if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
        fprintf(stderr, "Error: unable to parse file \"%s\"\n", enc_file);
        goto done;
    }

    /* find start node */
    node = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeEncryptedData, xmlSecEncNs);
    if(node == NULL) {
        fprintf(stderr, "Error: start node not found in \"%s\"\n", enc_file);
        goto done;
    }

    /* create encryption context, we don't need keys manager in this example */
    encCtx = xmlSecEncCtxCreate(NULL);
    if(encCtx == NULL) {
        fprintf(stderr,"Error: failed to create encryption context\n");
        goto done;
    }

    /* load DES key */
    encCtx->encKey = xmlSecKeyReadBinaryFile(xmlSecKeyDataDesId, key_file);
    if(encCtx->encKey == NULL) {
        fprintf(stderr,"Error: failed to load des key from binary file \"%s\"\n", key_file);
        goto done;
    }

    /* set the key name to the file name; this is only an example */
    if(xmlSecKeySetName(encCtx->encKey, BAD_CAST key_file) < 0) {
        fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", key_file);
        goto done;
    }

    /* decrypt the data */
    if((xmlSecEncCtxDecrypt(encCtx, node) < 0) || (encCtx->result == NULL)) {
        fprintf(stderr,"Error: decryption failed\n");
        goto done;
    }

    /* print decrypted data to stdout */
    if(encCtx->resultReplaced != 0) {
        fprintf(stdout, "Decrypted XML data:\n");
        xmlDocDump(stdout, doc);
    } else {
        fprintf(stdout, "Decrypted binary data (" XMLSEC_SIZE_FMT " bytes):\n",
            xmlSecBufferGetSize(encCtx->result));
        if(xmlSecBufferGetData(encCtx->result) != NULL) {
            fwrite(xmlSecBufferGetData(encCtx->result),
                  1,
                  xmlSecBufferGetSize(encCtx->result),
                  stdout);
        }
    }
    fprintf(stdout, "\n");

    /* success */
    res = 0;

done:
    /* cleanup */
    if(encCtx != NULL) {
        xmlSecEncCtxDestroy(encCtx);
    }

    if(doc != NULL) {
        xmlFreeDoc(doc);
    }
    return(res);
}

Full Program Listing

Clone this wiki locally