Sample code for 30+ languages & platforms
PureBasic

IPS MX Signature - Digitally Sign MX Document

See more XML Digital Signatures Examples

Demonstrates how to digitally sign ISO 20022 SWIFT MX messages.

Chilkat PureBasic Downloads

PureBasic
IncludeFile "CkPublicKey.pb"
IncludeFile "CkXml.pb"
IncludeFile "CkXmlDSig.pb"
IncludeFile "CkXmlDSigGen.pb"
IncludeFile "CkStringBuilder.pb"
IncludeFile "CkCert.pb"

Procedure ChilkatExample()

    success.i = 0

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

    success = 1

    ; First create the XML to be signed, or load it from a file, or a string,

    ; To load XML from a file:
    xmlToSign.i = CkXml::ckCreate()
    If xmlToSign.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkXml::ckLoadXmlFile(xmlToSign,"c:/someDir/mx_document.xml")

    ; Or to load XML from a string
    success = CkXml::ckLoadXml(xmlToSign,"...")

    ; Or create the XML directly.
    CkXml::ckClear(xmlToSign)

    ; Use this online tool to generate code from sample XML: 
    ; Generate Code to Create XML

    CkXml::setCkTag(xmlToSign, "DataPDU")
    CkXml::ckAddAttribute(xmlToSign,"xmlns","urn:cma:stp:xsd:stp.1.0")
    CkXml::ckUpdateAttrAt(xmlToSign,"Body|AppHdr",1,"xmlns","urn:iso:std:iso:20022:tech:xsd:head.001.001.01")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|AppHdr|Fr|FIId|FinInstnId|BICFI","ZZZZZZZZ")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|AppHdr|To|FIId|FinInstnId|BICFI","YYYYYYYYYY")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|AppHdr|BizMsgIdr","ZZZZZZZZAXXX999999999999999999999")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|AppHdr|MsgDefIdr","pacs.008.001.08")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|AppHdr|BizSvc","IPS")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|AppHdr|CreDt","2017-09-13T18:18:00Z")
    CkXml::ckUpdateAttrAt(xmlToSign,"Body|Document",1,"xmlns","urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|GrpHdr|MsgId","ZZZZZZZZAXXX999999999999999999999")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|GrpHdr|CreDtTm","2017-09-13T18:18:00")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|GrpHdr|NbOfTxs","1")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|GrpHdr|SttlmInf|SttlmMtd","CLRG")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|PmtId|EndToEndId","NOTPROVIDED")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|PmtId|TxId","ZZZZZZZZAXXX999999999999999999999")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|PmtTpInf|ClrChanl","RTNS")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|PmtTpInf|LclInstrm|Prtry","CSCT")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|PmtTpInf|CtgyPurp|Prtry","001")
    CkXml::ckUpdateAttrAt(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|IntrBkSttlmAmt",1,"Ccy","JOD")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|IntrBkSttlmAmt","71.12")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|IntrBkSttlmDt","2018-01-14")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|ChrgBr","SLEV")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|InstgAgt|FinInstnId|BICFI","ZZZZZZZZ")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|InstdAgt|FinInstnId|BICFI","UBSIJOA0")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|Dbtr|Nm","John Johnson")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|DbtrAcct|Id|IBAN","JO22CITI00000000000555555555")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|DbtrAgt|FinInstnId|BICFI","ZZZZZZZZ")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|DbtrAgt|FinInstnId|Othr|Id","200004")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|DbtrAgt|FinInstnId|Othr|SchmeNm|Prtry","1700099999")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|DbtrAgtAcct|Id|IBAN","JO66CITI22222222222222222222")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|CdtrAgt|FinInstnId|BICFI","UBSIJOA0")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|CdtrAgt|FinInstnId|Othr|Id","210027")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|CdtrAgt|FinInstnId|Othr|SchmeNm|Prtry","1400199999")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|CdtrAgtAcct|Id|IBAN","JO44UBSI33333333333333333333")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|Cdtr|Nm","Omega Jones")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|CdtrAcct|Id|IBAN","JO95UBSI00000000000777777777")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|InstrForNxtAgt|InstrInf","/BNF/Details")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|Purp|Prtry","5814")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|RgltryRptg|Dtls|Inf","SOMEINFORMATIONABOUTPAYMENT-1")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|RgltryRptg|Dtls|Inf[1]","SOMEINFORMATIONABOUTPAYMENT-2")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|RgltryRptg|Dtls|Inf[2]","SOMEINFORMATIONABOUTPAYMENT-3")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|Tax|Cdtr|TaxId","9900083901")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|Tax|Dbtr|TaxId","1000387561")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|RmtInf|Ustrd","EDV UCUN ODENIR")
    CkXml::ckUpdateChildContent(xmlToSign,"Body|Document|FIToFICstmrCdtTrf|CdtTrfTxInf|RmtInf|Ustrd[1]","EXTRA INFO")

    ; The following XML is to be signed:

    ; <?xml version="1.0" encoding="UTF-8"?>
    ; <DataPDU xmlns="urn:cma:stp:xsd:stp.1.0">
    ; 	<Body>
    ; 		<AppHdr xmlns="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">
    ; 			<Fr>
    ; 				<FIId>
    ; 					<FinInstnId>
    ; 						<BICFI>ZZZZZZZZ</BICFI>
    ; 					</FinInstnId>
    ; 				</FIId>
    ; 			</Fr>
    ; 			<To>
    ; 				<FIId>
    ; 					<FinInstnId>
    ; 						<BICFI>YYYYYYYYYY</BICFI>
    ; 					</FinInstnId>
    ; 				</FIId>
    ; 			</To>
    ; 			<BizMsgIdr>ZZZZZZZZAXXX999999999999999999999</BizMsgIdr>
    ; 			<MsgDefIdr>pacs.008.001.08</MsgDefIdr>
    ; 			<BizSvc>IPS</BizSvc>
    ; 			<CreDt>2017-09-13T18:18:00Z</CreDt>
    ; 		</AppHdr>
    ; 		<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08">
    ; 			<FIToFICstmrCdtTrf>
    ; 				<GrpHdr>
    ; 					<MsgId>ZZZZZZZZAXXX999999999999999999999</MsgId>
    ; 					<CreDtTm>2017-09-13T18:18:00</CreDtTm>
    ; 					<NbOfTxs>1</NbOfTxs>
    ; 					<SttlmInf>
    ; 						<SttlmMtd>CLRG</SttlmMtd>
    ; 					</SttlmInf>
    ; 				</GrpHdr>
    ; 				<CdtTrfTxInf>
    ; 					<PmtId>
    ; 						<EndToEndId>NOTPROVIDED</EndToEndId>
    ; 						<TxId>ZZZZZZZZAXXX999999999999999999999</TxId>
    ; 					</PmtId>
    ; 					<PmtTpInf>
    ; 						<ClrChanl>RTNS</ClrChanl>
    ; 						<LclInstrm>
    ; 							<Prtry>CSCT</Prtry>
    ; 						</LclInstrm>
    ; 						<CtgyPurp>
    ; 							<Prtry>001</Prtry>
    ; 						</CtgyPurp>
    ; 					</PmtTpInf>
    ; 					<IntrBkSttlmAmt Ccy="JOD">71.12</IntrBkSttlmAmt>
    ; 					<IntrBkSttlmDt>2018-01-14</IntrBkSttlmDt>
    ; 					<ChrgBr>SLEV</ChrgBr>
    ; 					<InstgAgt>
    ; 						<FinInstnId>
    ; 							<BICFI>ZZZZZZZZ</BICFI>
    ; 						</FinInstnId>
    ; 					</InstgAgt>
    ; 					<InstdAgt>
    ; 						<FinInstnId>
    ; 							<BICFI>UBSIJOA0</BICFI>
    ; 						</FinInstnId>
    ; 					</InstdAgt>
    ; 					<Dbtr>
    ; 						<Nm>John Johnson</Nm>
    ; 					</Dbtr>
    ; 					<DbtrAcct>
    ; 						<Id>
    ; 							<IBAN>JO22CITI00000000000555555555</IBAN>
    ; 						</Id>
    ; 					</DbtrAcct>
    ; 					<DbtrAgt>
    ; 						<FinInstnId>
    ; 							<BICFI>ZZZZZZZZ</BICFI>
    ; 							<Othr>
    ; 								<Id>200004</Id>
    ; 								<SchmeNm>
    ; 									<Prtry>1700089999</Prtry>
    ; 								</SchmeNm>
    ; 							</Othr>
    ; 						</FinInstnId>
    ; 					</DbtrAgt>
    ; 					<DbtrAgtAcct>
    ; 						<Id>
    ; 							<IBAN>JO66CITI22222222222222222222</IBAN>
    ; 						</Id>
    ; 					</DbtrAgtAcct>
    ; 					<CdtrAgt>
    ; 						<FinInstnId>
    ; 							<BICFI>UBSIJOA0</BICFI>
    ; 							<Othr>
    ; 								<Id>210027</Id>
    ; 								<SchmeNm>
    ; 									<Prtry>1400199999</Prtry>
    ; 								</SchmeNm>
    ; 							</Othr>
    ; 						</FinInstnId>
    ; 					</CdtrAgt>
    ; 					<CdtrAgtAcct>
    ; 						<Id>
    ; 							<IBAN>JO44UBSI33333333333333333333</IBAN>
    ; 						</Id>
    ; 					</CdtrAgtAcct>
    ; 					<Cdtr>
    ; 						<Nm>Omega Jones</Nm>
    ; 					</Cdtr>
    ; 					<CdtrAcct>
    ; 						<Id>
    ; 							<IBAN>JO95UBSI00000000000777777777</IBAN>
    ; 						</Id>
    ; 					</CdtrAcct>
    ; 					<InstrForNxtAgt>
    ; 						<InstrInf>/BNF/Details</InstrInf>
    ; 					</InstrForNxtAgt>
    ; 					<Purp>
    ; 						<Prtry>5814</Prtry>
    ; 					</Purp>
    ; 					<RgltryRptg>
    ; 						<Dtls>
    ; 							<Inf>SOMEINFORMATIONABOUTPAYMENT-1</Inf>
    ; 							<Inf>SOMEINFORMATIONABOUTPAYMENT-2</Inf>
    ; 							<Inf>SOMEINFORMATIONABOUTPAYMENT-3</Inf>
    ; 						</Dtls>
    ; 					</RgltryRptg>
    ; 					<Tax>
    ; 						<Cdtr>
    ; 							<TaxId>9900083901</TaxId>
    ; 						</Cdtr>
    ; 						<Dbtr>
    ; 							<TaxId>1000387561</TaxId>
    ; 						</Dbtr>
    ; 					</Tax>
    ; 					<RmtInf>
    ; 						<Ustrd>EDV UCUN ODENIR</Ustrd>
    ; 						<Ustrd>EXTRA INFO</Ustrd>
    ; 					</RmtInf>
    ; 				</CdtTrfTxInf>
    ; 			</FIToFICstmrCdtTrf>
    ; 		</Document>
    ; 	</Body>
    ; </DataPDU>

    gen.i = CkXmlDSigGen::ckCreate()
    If gen.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkXmlDSigGen::setCkSigLocation(gen, "DataPDU|Body|AppHdr|Sgntr")
    CkXmlDSigGen::setCkSigLocationMod(gen, 0)
    CkXmlDSigGen::setCkSigNamespacePrefix(gen, "ds")
    CkXmlDSigGen::setCkSigNamespaceUri(gen, "http://www.w3.org/2000/09/xmldsig#")
    CkXmlDSigGen::setCkSignedInfoCanonAlg(gen, "EXCL_C14N")
    CkXmlDSigGen::setCkSignedInfoDigestMethod(gen, "sha256")

    ; Set the KeyInfoId before adding references..
    CkXmlDSigGen::setCkKeyInfoId(gen, "_f9f2c543-e50a-4a50-bd91-50155d27f7e2")

    ; Create an Object to be added to the Signature.
    object1.i = CkXml::ckCreate()
    If object1.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkXml::setCkTag(object1, "xades:QualifyingProperties")
    CkXml::ckAddAttribute(object1,"xmlns:xades","http://uri.etsi.org/01903/v1.3.2#")
    CkXml::ckUpdateAttrAt(object1,"xades:SignedProperties",1,"Id","_4ed8e0ed-f47c-4262-909b-0458532ce7aa-signedprops")
    CkXml::ckUpdateChildContent(object1,"xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime","TO BE GENERATED BY CHILKAT")

    CkXmlDSigGen::ckAddObject(gen,"",CkXml::ckGetXml(object1),"","")

    ; -------- Reference 1 --------
    CkXmlDSigGen::ckAddSameDocRef(gen,"_f9f2c543-e50a-4a50-bd91-50155d27f7e2","sha256","EXCL_C14N","","")

    ; -------- Reference 2 --------
    CkXmlDSigGen::ckAddObjectRef(gen,"_4ed8e0ed-f47c-4262-909b-0458532ce7aa-signedprops","sha256","EXCL_C14N","","http://uri.etsi.org/01903/v1.3.2#SignedProperties")

    ; -------- Reference 3 --------
    CkXmlDSigGen::ckAddSameDocRef(gen,"","sha256","EXCL_C14N","","")

    ; Provide a certificate + private key. (PFX password is test123)
    cert.i = CkCert::ckCreate()
    If cert.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkCert::ckLoadPfxFile(cert,"qa_data/pfx/cert_test123.pfx","test123")
    If success = 0
        Debug CkCert::ckLastErrorText(cert)
        CkXml::ckDispose(xmlToSign)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        ProcedureReturn
    EndIf

    CkXmlDSigGen::ckSetX509Cert(gen,cert,1)

    CkXmlDSigGen::setCkKeyInfoType(gen, "X509Data")
    CkXmlDSigGen::setCkX509Type(gen, "IssuerSerial")

    ; Load XML to be signed...
    sbXml.i = CkStringBuilder::ckCreate()
    If sbXml.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkXml::ckGetXmlSb(xmlToSign,sbXml)

    ; Can alternatively use "CompactSignedXml"
    CkXmlDSigGen::setCkBehaviors(gen, "IndentedSignature,LocalSigningTime")

    ; Sign the XML...
    success = CkXmlDSigGen::ckCreateXmlDSigSb(gen,sbXml)
    If success = 0
        Debug CkXmlDSigGen::ckLastErrorText(gen)
        CkXml::ckDispose(xmlToSign)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        CkStringBuilder::ckDispose(sbXml)
        ProcedureReturn
    EndIf

    ; -----------------------------------------------

    ; Save the signed XML to a file.
    success = CkStringBuilder::ckWriteFile(sbXml,"qa_output/mx_signed.xml","utf-8",0)

    Debug CkStringBuilder::ckGetAsString(sbXml)

    ; ----------------------------------------
    ; Verify the signatures we just produced...
    verifier.i = CkXmlDSig::ckCreate()
    If verifier.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkXmlDSig::ckLoadSignatureSb(verifier,sbXml)
    If success = 0
        Debug CkXmlDSig::ckLastErrorText(verifier)
        CkXml::ckDispose(xmlToSign)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        CkStringBuilder::ckDispose(sbXml)
        CkXmlDSig::ckDispose(verifier)
        ProcedureReturn
    EndIf

    ; Important: The above signature did not include the full X.509 certificate.
    ; You must call verifier.SetPublicKey to provide the public key of the certificate required for validation.

    verifyCert.i = CkCert::ckCreate()
    If verifyCert.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkCert::ckLoadFromFile(verifyCert,"qa_data/certs/cert_test123.cer")
    If success = 0
        Debug CkCert::ckLastErrorText(verifyCert)
        CkXml::ckDispose(xmlToSign)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        CkStringBuilder::ckDispose(sbXml)
        CkXmlDSig::ckDispose(verifier)
        CkCert::ckDispose(verifyCert)
        ProcedureReturn
    EndIf

    pubKey.i = CkPublicKey::ckCreate()
    If pubKey.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkCert::ckGetPublicKey(verifyCert,pubKey)

    CkXmlDSig::ckSetPublicKey(verifier,pubKey)

    numSigs.i = CkXmlDSig::ckNumSignatures(verifier)
    verifyIdx.i = 0
    While verifyIdx < numSigs
        CkXmlDSig::setCkSelector(verifier, verifyIdx)
        verified.i = CkXmlDSig::ckVerifySignature(verifier,1)
        If verified <> 1
            Debug CkXmlDSig::ckLastErrorText(verifier)
            CkXml::ckDispose(xmlToSign)
            CkXmlDSigGen::ckDispose(gen)
            CkXml::ckDispose(object1)
            CkCert::ckDispose(cert)
            CkStringBuilder::ckDispose(sbXml)
            CkXmlDSig::ckDispose(verifier)
            CkCert::ckDispose(verifyCert)
            CkPublicKey::ckDispose(pubKey)
            ProcedureReturn
        EndIf

        verifyIdx = verifyIdx + 1
    Wend
    Debug "All signatures were successfully verified."


    CkXml::ckDispose(xmlToSign)
    CkXmlDSigGen::ckDispose(gen)
    CkXml::ckDispose(object1)
    CkCert::ckDispose(cert)
    CkStringBuilder::ckDispose(sbXml)
    CkXmlDSig::ckDispose(verifier)
    CkCert::ckDispose(verifyCert)
    CkPublicKey::ckDispose(pubKey)


    ProcedureReturn
EndProcedure