Discuss this help topic in SecureBlackbox Forum

XML: Create custom signature

The xml signature could have neither enveloping nor enveloped nor detached signature type.

For example, the signature and data signed can be present in the same XML document as sibling elements (sometimes it is called internally detached signature):

Signed document:


<root>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
      <ds:Reference URI="#data">
        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <ds:DigestValue>...</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>...</ds:SignatureValue/>
    <ds:KeyInfo>...</ds:KeyInfo>
  </ds:Signature>

  <document Id="data">
     <title>Title</title>
     <content>Text...</content>
  </document>
</root>

Code sample:

C#:


void Sign(TElXMLDOMElement SignedElement,
  TElXMLDOMElement SignatureParentElement, TElX509Certificate Cert)
{
  TElXMLSigner Signer = new TElXMLSigner(null);
  TElXMLKeyInfoX509Data X509Data = new TElXMLKeyInfoX509Data(false);
  try
  {
    Signer.SignatureType = SBXMLSec.Unit.xstEnveloped;
    Signer.CanonicalizationMethod = SBXMLDefs.Unit.xcmCanon;
    Signer.SignatureMethodType = SBXMLSec.Unit.xmtSig;
    Signer.SignatureMethod = SBXMLSec.Unit.xsmRSA_SHA1;

    TElXMLReference Ref = new TElXMLReference();
    Ref.DigestMethod = SBXMLSec.Unit.xdmSHA1;
    Ref.URI = "#" + SBXMLUtils.Unit.GetElementId(SignedElement);
    Ref.URINode = SignedElement;
    Signer.References.Add(Ref);

    X509Data.Certificate = Cert;
    Signer.KeyData = X509Data;

    Signer.UpdateReferencesDigest();
    Signer.GenerateSignature();

    Signer.SaveEnveloped(SignatureParentElement);
  }
  finally
  {
    Signer.Dispose();
    X509Data.Dispose();
  }
}
Delphi:

procedure Sign(SignedElement : TElXMLDOMElement;
  SignatureParentElement : TElXMLDOMElement; Cert : TElX509Certificate);
var
  Signer: TElXMLSigner;
  X509Data: TElXMLKeyInfoX509Data;
  Ref: TElXMLReference;
begin
  Signer:= TElXMLSigner.Create(nil);
  X509Data := TElXMLKeyInfoX509Data.Create(false);
  try
    Signer.SignatureType := xstEnveloped;
    Signer.CanonicalizationMethod := xcmCanon;
    Signer.SignatureMethodType := xmtSig;
    Signer.SignatureMethod := xsmRSA_SHA1;

    Ref := TElXMLReference.Create;
    Ref.DigestMethod := xdmSHA1;
    Ref.URI := '#' + GetElementId(SignedElement);
    Ref.URINode := SignedElement;

    Signer.References.Add(Ref);

    X509Data.Certificate := Cert;
    Signer.KeyData := X509Data;

    Signer.UpdateReferencesDigest;

    Signer.GenerateSignature;

    Signer.SaveEnveloped(SignatureParentElement);
  finally
    FreeAndNil(Signer);
    FreeAndNil(X509Data);
  end;
end;

In this code we save a signature using enveloped signature type as the most similar signature type, but without enveloped signature transform, as we don't need to exclude <ds:Signature> element from reference computation.

The Save*() methods works as follows:

  • If the enveloped signature type is used, then TElXMLSigner.SaveEnveloped() method saves a signature as a last child of the element, passed as a parameter.
  • If the enveloping signature type is used, then TElXMLSigner.SaveEnveloping() method places a node, passed as a parameter, into <ds:Object> element in the signature and then replaces this node with the newly created <ds:Signature> element.
  • If the detached signature type is used, then TElXMLSigner.SaveDetached() method creates new TElXMLDOMDocument instance and saves a signature as a document element

Depending on the situation you can choose any of those methods to save a custom signature type. For example, a signature could have several references: the first is referencing the data in a sibling element, and the second is referencing the data that should be placed in enveloping object. Consider this example:


<root>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
      <ds:Reference URI="#data">
        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <ds:DigestValue>...</ds:DigestValue>
      </ds:Reference>
      <ds:Reference URI="#objId">
        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <ds:DigestValue>...</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>...</ds:SignatureValue/>
    <ds:KeyInfo>...</ds:KeyInfo>
    <ds:Object Id="objId"><data>Hello, World!</data></ds:Object>
  </ds:Signature>

  <document Id="data">
     <title>Title</title>
     <content>Text...</content>
  </document>
</root>

In this case using enveloping signature type would be simpler, but it is possible to achieve similar signature using enveloped signature type by creating a custom object.

How To articles about XML signing (XMLDSig and XAdES)

Discuss this help topic in SecureBlackbox Forum