C语言 使用xmlsec对XML的一部分进行签名



我正在尝试签署一个简单的soap消息。消息看起来像这样:

<?xml version="1.0" encoding="utf-8"?>
<e:Envelope xmlns:e="http://schemas.xmlsoap.org/soap/envelope/">
<e:Header />
<e:Body>
<Data>someData</Data>
</e:Body>
</e:Envelope>
我绝不是网络和安全方面的专家。据我所知,结果应该是这样的:
<?xml version="1.0" encoding="utf-8"?>
<e:Envelope xmlns:e="http://schemas.xmlsoap.org/soap/envelope/">
<e:Header>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
.... 
<Reference URI="#signedContent">
....
</Signature>
</e:Header>
<e:Body id="signedContent">
<Data>someData</Data>
</e:Body>
</e:Envelope>

所以我的理解是签名在头部,签名引用指向正文。我通过调用

将签名节点放入标题中
xmlAddChild(xmlDocGetRootElement(doc)->children, signNode);

然而,我不知道如何告诉图书馆签名的身体。我是否必须自己在主体中放置id属性,还是应该让库来做?该库没有很好的文档(或者可能这些函数有意义,但不适合像我这样的人)。接下来需要调用哪些函数(我使用x509示例作为基础:https://www.aleksey.com/xmlsec/api/xmlsec-examples-sign-x509.html#xmlsec-example-sign3)。也许我应该像这样创建参考节点:

xmlNodePtr refNode = xmlSecTmplSignatureAddReference(signNode, xmlSecTransformSha256Id,
NULL, (const xmlChar*)"signedContent”, NULL);
但是我一点也不确定。我看到还有其他函数,如xmlSecTmplReferenceAddTransform。xmlsec网页上的示例使用xmlsectransformenveledid来调用它,但我不需要使用enveled,也许我应该使用其他东西来调用它。

如果不阅读库的FAQ,就会发生这种情况。首先,不需要动态创建签名模板。如果您像我一样手动构建SOAP,那么将其设置为像这样的文字就足够了:

<?xml version="1.0" encoding="utf-8"?>
<e:Envelope xmlns:e="http://schemas.xmlsoap.org/soap/envelope/">
<e:Header><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#signedContent">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue></DigestValue>
</Reference>
</SignedInfo>
<SignatureValue></SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate></X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</e:Header>
<e:Body id="signedContent"><Data>SomeData<Data>
</e:Body>
</e:Envelope>

下一步是将其传递给xmlsec。可以看到,digestValue、signedValue和x509都为空。我使用c++,所以我将它作为字符串传递,并附带来自HSM的x509证书和私钥(我使用libp11访问它):

std::string XmlSigner::signSoapTemplate(const std::string& document, evp_pkey_st* prvKey, const std::string& x509)
{
if (!init && !initialize()) {
return "xmlsec could not be initialized";
}
init = true;

if (prvKey == nullptr) return{};
if (x509.empty()) return{};
std::string result;
xmlChar* signedBuffer{ nullptr };
int signedLength{ 0 };
/* load doc file */
xmlDocPtr doc = xmlParseMemory(document.data(), document.size());
if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)) {
return "Error: unable to parse file";
}

xmlAttrPtr attr = xmlDocGetRootElement(doc)->children->next->properties;
//THIS IS THE MOST IMPORTANT LINE:
xmlAddID(NULL, doc, (const xmlChar*)"signedContent", attr);
xmlNodePtr signNode = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs);
if (signNode == NULL) {
return "Error: start node not found";
}
/* create signature context */
xmlSecDSigCtxPtr dsigCtx = xmlSecDSigCtxCreate(NULL);
if (dsigCtx == NULL) {
return "Error: failed to create signature contextn";
}
dsigCtx->enabledReferenceUris = xmlSecTransformUriTypeSameDocument;
dsigCtx->signKey = xmlSecKeyCreate();
dsigCtx->signKey->value = xmlSecOpenSSLEvpKeyAdopt(prvKey);
if (
xmlSecOpenSSLAppKeyCertLoadMemory(
dsigCtx->signKey,
reinterpret_cast<const unsigned char*>(x509.data()),
x509.size(),
xmlSecKeyDataFormatPem) < 0
)
{ return "Failed to load certificate"; }

/* sign the template */
if (xmlSecDSigCtxSign(dsigCtx, signNode) < 0) {
return "Error: signature failedn";
}
/* dumps the memory to the buffer */
xmlDocDumpMemory(doc, &signedBuffer, &signedLength);
result = std::string(reinterpret_cast<char*>(signedBuffer), signedLength);
/* success */

/* cleanup */
if (dsigCtx != NULL) {
xmlSecDSigCtxDestroy(dsigCtx);
}
if (doc != NULL) {
xmlFreeDoc(doc);
}
return result;
}

错误处理不应该用返回字符串来完成,但是——这只是一个例子。

相关内容

  • 没有找到相关文章

最新更新