2.4.5. Цифровая подпись XML сертификатом ГОСТ3411¶
Подпись XML предлагается генерировать с помощью CryptoAPI
и LibXml2.
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"/><ds:Reference URI="#foo"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/><ds:DigestValue/></ds:Reference><ds:Reference URI="#signed-props" Type="http://uri.etsi.org/01903#SignedProperties"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/><ds:KeyInfo><ds:X509Data><ds:X509Certificate/></ds:X509Data></ds:KeyInfo><ds:Object><xades:QualifyingProperties Target="#xmldsig" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"><xades:SignedProperties Id="signed-props"><xades:SignedSignatureProperties><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/><ds:DigestValue/></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName/><ds:X509SerialNumber/></xades:IssuerSerial></xades:Cert></xades:SigningCertificate></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object></ds:Signature>
<PASCAL AXml, AId, ASignPattern>
uses
XML;
function GetDigest(AData, ACert, AToSign): string;
begin
hash := Cryptor.CreateHashByCert(ACert);
hash.HashData(AData);
if AToSign then
begin
Result := hash.Sign;
Result := BlobRevers(Result);
end else
Result := hash.Value
Result := BlobToBase64(Result);
end;
procedure PutCanonicDigest(ADoc, AXPathCtx, ACertToDigest, AExclusiveCanon, ASignature, ADataToDigestXPath, ADigestValueNodeXPath);
begin
cData := AXPathCtx.FindNodes(ADataToDigestXPath);
cText := ADoc.C14nCanonicalize(AExclusiveCanon, cData);
AXPath.FindNode(ADigestValueNodeXPath).Text := GetDigest(StringToBlob(cText), ACertToDigest, ASignature);
end;
SignDoc := CreateXmlDocFromString(ASignPattern);
Doc := CreateXmlDocFromString(AXml);
XPath := Doc.CreateXPathContext;
XPath.AddNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
XPath.AddNamespace('xades', 'http://uri.etsi.org/01903/v1.3.2#');
// Встроить шаблон подписи в документ
XPath.FindNode('//*[@Id="'+AId+'"]').Child.AddPrevSibling(SignDoc.Root);
XPath.FindNode('//*[@URI="#foo"]').Attributes['URI'] := '#'+AId;
SignDoc := nil;
// Заполнить поля подписи данными сертификата
Cert := Cryptor.CreateCertByDialog;
XPath.FindNode('//ds:X509Certificate').Text := BlobToBase64(Cert.RawData);
XPath.FindNode('//xades:CertDigest/ds:DigestValue').Text := GetDigest(Cert.RawData, Cert, False);
XPath.FindNode('//ds:X509IssuerName').Text := Cert.IssuerName(cnfRFC1779);
XPath.FindNode('//ds:X509SerialNumber').Text := BlobIntToStr(Cert.SerialNumber);
// рассчитать хеши подписи
PutCanonicDigest(Doc, XPath, Cert, True, False,
'(//. | //@* | //namespace::*)[ancestor-or-self::*[@Id="'+AId+'"]][not(ancestor-or-self::ds:Signature)]',
'//*[@URI="#'+AId+'"]/ds:DigestValue');
PutCanonicDigest(Doc, XPath, Cert, False, False,
'(//.|//@*|//namespace::*)[ancestor-or-self::xades:SignedProperties[1]]',
'//*[@URI=concat("#", //xades:SignedProperties[1]/@Id)]/ds:DigestValue');
PutCanonicDigest(Doc, XPath, Cert, False, True,
'(//. | //@* | //namespace::*)[ancestor-or-self::ds:SignedInfo]',
'//ds:Signature[1]/ds:SignatureValue');
Result := Doc.ToString;
</PASCAL>
Note
XPath-запрос для каноникализации и расчёта хешей должен возвращать не только тот узел что планируется подписать, но и все его подузлы вместе с атрибутами и объявленными namespace-ами.
Например:
//ds:Body
- неправильный запрос, он возвращает только сам элемент Body.
Правильно будет так:
(//. | //@* | //namespace::*)[ancestor-or-self::ds:Body]
- запрос возвращает Body целиком, со всем его содержимым.