Sample code for 30+ languages & platforms
Unicode C

IPS MX Signature - Digitally Sign MX Document

See more XML Digital Signatures Examples

Demonstrates how to digitally sign ISO 20022 SWIFT MX messages.

Chilkat Unicode C Downloads

Unicode C
#include <C_CkXmlW.h>
#include <C_CkXmlDSigGenW.h>
#include <C_CkCertW.h>
#include <C_CkStringBuilderW.h>
#include <C_CkXmlDSigW.h>
#include <C_CkPublicKeyW.h>

void ChilkatSample(void)
    {
    BOOL success;
    HCkXmlW xmlToSign;
    HCkXmlDSigGenW gen;
    HCkXmlW object1;
    HCkCertW cert;
    HCkStringBuilderW sbXml;
    HCkXmlDSigW verifier;
    HCkCertW verifyCert;
    HCkPublicKeyW pubKey;
    int numSigs;
    int verifyIdx;
    BOOL verified;

    success = FALSE;

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

    success = TRUE;

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

    // To load XML from a file:
    xmlToSign = CkXmlW_Create();
    success = CkXmlW_LoadXmlFile(xmlToSign,L"c:/someDir/mx_document.xml");

    // Or to load XML from a string
    success = CkXmlW_LoadXml(xmlToSign,L"...");

    // Or create the XML directly.
    CkXmlW_Clear(xmlToSign);

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

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

    CkXmlDSigGenW_putSigLocation(gen,L"DataPDU|Body|AppHdr|Sgntr");
    CkXmlDSigGenW_putSigLocationMod(gen,0);
    CkXmlDSigGenW_putSigNamespacePrefix(gen,L"ds");
    CkXmlDSigGenW_putSigNamespaceUri(gen,L"http://www.w3.org/2000/09/xmldsig#");
    CkXmlDSigGenW_putSignedInfoCanonAlg(gen,L"EXCL_C14N");
    CkXmlDSigGenW_putSignedInfoDigestMethod(gen,L"sha256");

    // Set the KeyInfoId before adding references..
    CkXmlDSigGenW_putKeyInfoId(gen,L"_f9f2c543-e50a-4a50-bd91-50155d27f7e2");

    // Create an Object to be added to the Signature.
    object1 = CkXmlW_Create();
    CkXmlW_putTag(object1,L"xades:QualifyingProperties");
    CkXmlW_AddAttribute(object1,L"xmlns:xades",L"http://uri.etsi.org/01903/v1.3.2#");
    CkXmlW_UpdateAttrAt(object1,L"xades:SignedProperties",TRUE,L"Id",L"_4ed8e0ed-f47c-4262-909b-0458532ce7aa-signedprops");
    CkXmlW_UpdateChildContent(object1,L"xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime",L"TO BE GENERATED BY CHILKAT");

    CkXmlDSigGenW_AddObject(gen,L"",CkXmlW_getXml(object1),L"",L"");

    // -------- Reference 1 --------
    CkXmlDSigGenW_AddSameDocRef(gen,L"_f9f2c543-e50a-4a50-bd91-50155d27f7e2",L"sha256",L"EXCL_C14N",L"",L"");

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

    // -------- Reference 3 --------
    CkXmlDSigGenW_AddSameDocRef(gen,L"",L"sha256",L"EXCL_C14N",L"",L"");

    // Provide a certificate + private key. (PFX password is test123)
    cert = CkCertW_Create();
    success = CkCertW_LoadPfxFile(cert,L"qa_data/pfx/cert_test123.pfx",L"test123");
    if (success == FALSE) {
        wprintf(L"%s\n",CkCertW_lastErrorText(cert));
        CkXmlW_Dispose(xmlToSign);
        CkXmlDSigGenW_Dispose(gen);
        CkXmlW_Dispose(object1);
        CkCertW_Dispose(cert);
        return;
    }

    CkXmlDSigGenW_SetX509Cert(gen,cert,TRUE);

    CkXmlDSigGenW_putKeyInfoType(gen,L"X509Data");
    CkXmlDSigGenW_putX509Type(gen,L"IssuerSerial");

    // Load XML to be signed...
    sbXml = CkStringBuilderW_Create();
    CkXmlW_GetXmlSb(xmlToSign,sbXml);

    // Can alternatively use "CompactSignedXml"
    CkXmlDSigGenW_putBehaviors(gen,L"IndentedSignature,LocalSigningTime");

    // Sign the XML...
    success = CkXmlDSigGenW_CreateXmlDSigSb(gen,sbXml);
    if (success == FALSE) {
        wprintf(L"%s\n",CkXmlDSigGenW_lastErrorText(gen));
        CkXmlW_Dispose(xmlToSign);
        CkXmlDSigGenW_Dispose(gen);
        CkXmlW_Dispose(object1);
        CkCertW_Dispose(cert);
        CkStringBuilderW_Dispose(sbXml);
        return;
    }

    // -----------------------------------------------

    // Save the signed XML to a file.
    success = CkStringBuilderW_WriteFile(sbXml,L"qa_output/mx_signed.xml",L"utf-8",FALSE);

    wprintf(L"%s\n",CkStringBuilderW_getAsString(sbXml));

    // ----------------------------------------
    // Verify the signatures we just produced...
    verifier = CkXmlDSigW_Create();
    success = CkXmlDSigW_LoadSignatureSb(verifier,sbXml);
    if (success == FALSE) {
        wprintf(L"%s\n",CkXmlDSigW_lastErrorText(verifier));
        CkXmlW_Dispose(xmlToSign);
        CkXmlDSigGenW_Dispose(gen);
        CkXmlW_Dispose(object1);
        CkCertW_Dispose(cert);
        CkStringBuilderW_Dispose(sbXml);
        CkXmlDSigW_Dispose(verifier);
        return;
    }

    // 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 = CkCertW_Create();
    success = CkCertW_LoadFromFile(verifyCert,L"qa_data/certs/cert_test123.cer");
    if (success == FALSE) {
        wprintf(L"%s\n",CkCertW_lastErrorText(verifyCert));
        CkXmlW_Dispose(xmlToSign);
        CkXmlDSigGenW_Dispose(gen);
        CkXmlW_Dispose(object1);
        CkCertW_Dispose(cert);
        CkStringBuilderW_Dispose(sbXml);
        CkXmlDSigW_Dispose(verifier);
        CkCertW_Dispose(verifyCert);
        return;
    }

    pubKey = CkPublicKeyW_Create();
    CkCertW_GetPublicKey(verifyCert,pubKey);

    CkXmlDSigW_SetPublicKey(verifier,pubKey);

    numSigs = CkXmlDSigW_getNumSignatures(verifier);
    verifyIdx = 0;
    while (verifyIdx < numSigs) {
        CkXmlDSigW_putSelector(verifier,verifyIdx);
        verified = CkXmlDSigW_VerifySignature(verifier,TRUE);
        if (verified != TRUE) {
            wprintf(L"%s\n",CkXmlDSigW_lastErrorText(verifier));
            CkXmlW_Dispose(xmlToSign);
            CkXmlDSigGenW_Dispose(gen);
            CkXmlW_Dispose(object1);
            CkCertW_Dispose(cert);
            CkStringBuilderW_Dispose(sbXml);
            CkXmlDSigW_Dispose(verifier);
            CkCertW_Dispose(verifyCert);
            CkPublicKeyW_Dispose(pubKey);
            return;
        }

        verifyIdx = verifyIdx + 1;
    }

    wprintf(L"All signatures were successfully verified.\n");


    CkXmlW_Dispose(xmlToSign);
    CkXmlDSigGenW_Dispose(gen);
    CkXmlW_Dispose(object1);
    CkCertW_Dispose(cert);
    CkStringBuilderW_Dispose(sbXml);
    CkXmlDSigW_Dispose(verifier);
    CkCertW_Dispose(verifyCert);
    CkPublicKeyW_Dispose(pubKey);

    }