Sample code for 30+ languages & platforms
Xbase++

Sign SOAP XML for New Zealand Customs Service

See more XAdES Examples

Demonstrates how to create an XAdES signed SOAP XML pertaining to the New Zealand Customs Service.

Note: This example requires Chilkat v9.5.0.96 or later.

Chilkat Xbase++ Downloads

Xbase++
LOCAL nSuccess
LOCAL oTsId
LOCAL oStrId
LOCAL oKeyInfoId
LOCAL oDt
LOCAL oSbNow
LOCAL n
LOCAL oSbNowPlusOneHour
LOCAL oXmlToSign
LOCAL oGen
LOCAL oXml1
LOCAL oXml2
LOCAL oCert
LOCAL oXmlCustomKeyInfo
LOCAL oSbXml

nSuccess := 0

//  This example assumes the Chilkat API to have been previously unlocked.
//  See Global Unlock Sample for sample code.

nSuccess := 1

//  Create the following XML to be signed:

//  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
//      xmlns:v1="http://customs.govt.nz/jbms/msggate/reqresp/v1">
//      <soapenv:Header>
//          <wsse:Security soapenv:mustUnderstand="1"
//              xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
//              xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
//              <wsu:Timestamp wsu:Id="TS-037E78514E9B9132CB16817563559151">
//                  <wsu:Created>2023-04-17T18:32:35.913Z</wsu:Created>
//                  <wsu:Expires>2023-04-17T19:32:35.913Z</wsu:Expires>
//              </wsu:Timestamp>
//          </wsse:Security>
//      </soapenv:Header>
//      <soapenv:Body wsu:Id="id-8"
//          xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
//          <v1:RequestResponse>
//              <v1:Submitter>TEST1234</v1:Submitter>
//              <v1:MailboxMsgId>999999</v1:MailboxMsgId>
//          </v1:RequestResponse>
//      </soapenv:Body>
//  </soapenv:Envelope>

//  Create a random ID like this: TS-037E78514E9B9132CB16817563559151
oTsId := CreateObject("Chilkat.StringBuilder")
oTsId:Append("TS-")
oTsId:AppendRandom(16, "hex")

//  STR-037E78514E9B9132CB16817563559614
oStrId := CreateObject("Chilkat.StringBuilder")
oStrId:Append("STR-")
oStrId:AppendRandom(16, "hex")

//  KI-037E78514E9B9132CB16817563559583
oKeyInfoId := CreateObject("Chilkat.StringBuilder")
oKeyInfoId:Append("KI-")
oKeyInfoId:AppendRandom(16, "hex")

//  Create a date/time for the current time with this format:  2023-04-17T18:32:35.913Z
oDt := CreateObject("Chilkat.CkDateTime")
oDt:SetFromCurrentSystemTime()

oSbNow := CreateObject("Chilkat.StringBuilder")
oSbNow:Append(oDt:GetAsTimestamp(0))
//  If we really need the milliseconds, we can replace the "Z" with ".000Z"
//  The server will also likely accept a timestamp without milliseconds, such as 2023-04-17T18:32:35Z
n := oSbNow:Replace("Z", ".000Z")

oSbNowPlusOneHour := CreateObject("Chilkat.StringBuilder")
oDt:AddSeconds(3600)
oSbNowPlusOneHour:Append(oDt:GetAsTimestamp(0))
n := oSbNowPlusOneHour:Replace("Z", ".000Z")

oXmlToSign := CreateObject("Chilkat.Xml")
oXmlToSign:Tag := "soapenv:Envelope"
oXmlToSign:AddAttribute("xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
oXmlToSign:AddAttribute("xmlns:v1", "http://customs.govt.nz/jbms/msggate/reqresp/v1")
oXmlToSign:UpdateAttrAt("soapenv:Header|wsse:Security", 1, "soapenv:mustUnderstand", "1")
oXmlToSign:UpdateAttrAt("soapenv:Header|wsse:Security", 1, "xmlns:wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")
oXmlToSign:UpdateAttrAt("soapenv:Header|wsse:Security", 1, "xmlns:wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd")
oXmlToSign:UpdateAttrAt("soapenv:Header|wsse:Security|wsu:Timestamp", 1, "wsu:Id", oTsId:GetAsString())
oXmlToSign:UpdateChildContent("soapenv:Header|wsse:Security|wsu:Timestamp|wsu:Created", oSbNow:GetAsString())
oXmlToSign:UpdateChildContent("soapenv:Header|wsse:Security|wsu:Timestamp|wsu:Expires", oSbNowPlusOneHour:GetAsString())
oXmlToSign:UpdateAttrAt("soapenv:Body", 1, "wsu:Id", "id-8")
oXmlToSign:UpdateAttrAt("soapenv:Body", 1, "xmlns:wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd")
oXmlToSign:UpdateChildContent("soapenv:Body|v1:RequestResponse|v1:Submitter", "TEST1234")
oXmlToSign:UpdateChildContent("soapenv:Body|v1:RequestResponse|v1:MailboxMsgId", "999999")

oGen := CreateObject("Chilkat.XmlDSigGen")

oGen:SigLocation := "soapenv:Envelope|soapenv:Header|wsse:Security"
oGen:SigLocationMod := 0
oGen:SigId := "SIG-037E78514E9B9132CB16817563559695"
oGen:SigNamespacePrefix := "ds"
oGen:SigNamespaceUri := "http://www.w3.org/2000/09/xmldsig#"
oGen:SignedInfoPrefixList := "soapenv v1"
oGen:IncNamespacePrefix := "ec"
oGen:IncNamespaceUri := "http://www.w3.org/2001/10/xml-exc-c14n#"
oGen:SignedInfoCanonAlg := "EXCL_C14N"
oGen:SignedInfoDigestMethod := "sha256"

//  Set the KeyInfoId before adding references..
oGen:KeyInfoId := oKeyInfoId:GetAsString()

//  -------- Reference 1 --------
oXml1 := CreateObject("Chilkat.Xml")
oXml1:Tag := "ds:Transforms"
oXml1:UpdateAttrAt("ds:Transform", 1, "Algorithm", "http://www.w3.org/2001/10/xml-exc-c14n#")
oXml1:UpdateAttrAt("ds:Transform|ec:InclusiveNamespaces", 1, "PrefixList", "wsse soapenv v1")
oXml1:UpdateAttrAt("ds:Transform|ec:InclusiveNamespaces", 1, "xmlns:ec", "http://www.w3.org/2001/10/xml-exc-c14n#")

oGen:AddSameDocRef2(oTsId:GetAsString(), "sha256", oXml1, "")

//  -------- Reference 2 --------
oXml2 := CreateObject("Chilkat.Xml")
oXml2:Tag := "ds:Transforms"
oXml2:UpdateAttrAt("ds:Transform", 1, "Algorithm", "http://www.w3.org/2001/10/xml-exc-c14n#")
oXml2:UpdateAttrAt("ds:Transform|ec:InclusiveNamespaces", 1, "PrefixList", "v1")
oXml2:UpdateAttrAt("ds:Transform|ec:InclusiveNamespaces", 1, "xmlns:ec", "http://www.w3.org/2001/10/xml-exc-c14n#")

oGen:AddSameDocRef2("id-8", "sha256", oXml2, "")

//  Provide a certificate + private key. (PFX password is test123)
oCert := CreateObject("Chilkat.Cert")
nSuccess := oCert:LoadPfxFile("qa_data/pfx/cert_test123.pfx", "test123")
IF (nSuccess != 1)
    ? oCert:LastErrorText
    oTsId:destroy()
    oStrId:destroy()
    oKeyInfoId:destroy()
    oDt:destroy()
    oSbNow:destroy()
    oSbNowPlusOneHour:destroy()
    oXmlToSign:destroy()
    oGen:destroy()
    oXml1:destroy()
    oXml2:destroy()
    oCert:destroy()
    RETURN
ENDIF

oGen:SetX509Cert(oCert, 1)

oGen:KeyInfoType := "Custom"

//  Create the custom KeyInfo XML..
oXmlCustomKeyInfo := CreateObject("Chilkat.Xml")
oXmlCustomKeyInfo:Tag := "wsse:SecurityTokenReference"
oXmlCustomKeyInfo:AddAttribute("wsu:Id", oStrId:GetAsString())
oXmlCustomKeyInfo:UpdateAttrAt("wsse:KeyIdentifier", 1, "EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary")
oXmlCustomKeyInfo:UpdateAttrAt("wsse:KeyIdentifier", 1, "ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3")
//  Insert the single-line base64 of the signing certificate's DER
oCert:UncommonOptions := "Base64CertNoCRLF"
oXmlCustomKeyInfo:UpdateChildContent("wsse:KeyIdentifier", oCert:GetEncoded())

oXmlCustomKeyInfo:EmitXmlDecl := 0
oGen:CustomKeyInfoXml := oXmlCustomKeyInfo:GetXml()

//  Load XML to be signed...
oSbXml := CreateObject("Chilkat.StringBuilder")
oXmlToSign:GetXmlSb(oSbXml)

oGen:Behaviors := "IndentedSignature"

//  Sign the XML...
oGen:VerboseLogging := 1
nSuccess := oGen:CreateXmlDSigSb(oSbXml)
IF (nSuccess != 1)
    ? oGen:LastErrorText
    oTsId:destroy()
    oStrId:destroy()
    oKeyInfoId:destroy()
    oDt:destroy()
    oSbNow:destroy()
    oSbNowPlusOneHour:destroy()
    oXmlToSign:destroy()
    oGen:destroy()
    oXml1:destroy()
    oXml2:destroy()
    oCert:destroy()
    oXmlCustomKeyInfo:destroy()
    oSbXml:destroy()
    RETURN
ENDIF

//  Save the signed XML to a file.
nSuccess := oSbXml:WriteFile("c:/temp/qa_output/signedXml.xml", "utf-8", 0)

? oSbXml:GetAsString()

oTsId:destroy()
oStrId:destroy()
oKeyInfoId:destroy()
oDt:destroy()
oSbNow:destroy()
oSbNowPlusOneHour:destroy()
oXmlToSign:destroy()
oGen:destroy()
oXml1:destroy()
oXml2:destroy()
oCert:destroy()
oXmlCustomKeyInfo:destroy()
oSbXml:destroy()