Using BankID C Server
This chapter shows code examples for different use cases with BankID C Server.
The code does not contain any error handling or assertions and assumes all calls succeed.
Application start-up code
Before the BankID C Server API can be used, the application will need to establish a session.
The merchant application needs to perform the following action
- Call the function BID_Initialize() to initialize the API
- Call the function BID_OpenSession() to initialize a merchant session
Merchants not using HSM:
int sessioncontext; res = BID_Initialize(); res = BID_OpenSession(&sessioncontext, "C:\\merchant1\\merchant_certificate.bid", "certificate_passphrase", " C:\\merchant1\\merchant.cfg", NULL,NULL,NULL,NULL,NULL,NULL);
Merchants using HSM:
int sessioncontext; res = BID_HSMInitialize("C:\\Programfiler\\LunaSA\\cryptoki.dll"); res = BID_HSMOpenSession(&sessioncontext, "C:\\merchant1\\merchant_certificate.bid", "certificate_passphrase", " C:\\merchant1\\merchant.cfg", NULL,NULL,NULL,NULL,NULL,NULL,"hsm_password");
The merchant application can be written on behalf of a single merchant or several merchants. To configure the system, BID_Initialize must be called once for the whole system. BID_OpenSession is called for every merchant that will be running. Configuration can be passed in as parameters to BID_OpenSession, or via a configuration file. In the example above, the merchant has chosen to enter parameters via the configuration file merchant.cfg. Therefore, all the remaining parameters to BID_OpenSession are empty. If BID_OpenSession returns successfully, the merchant application can use the services offered by BankID C Server.
Application shutdown code
When the merchant application has finished using BankID C Server, it should clean up all resources held by each merchant session and the BankID C Server globally.
The following must be done to clean up.
- Call the function BID_CloseSession for every merchant session running.
- Call the function BID_Finalize() to clean up global settings.
/* Do the below call for all merchant contexts */ res = BID_CloseSession(sessioncontext); res = BID_Finalize()
Initializing clients
Authentication with web-client
The code below shows an example of how the merchant initiates a transaction where the user uses the Web-client to authenticate.
/* *This code only shows how to set the infoitems and call the* *BID_InitSession method. */ char *helperUri = NULL; char *tid = NULL; char *cid = NULL; ret = BID_SetInfoItem(handle, "action", "auth"); ret = BID_SetInfoItem(handle, "merchanturl", "https://url.no/handleBankIDCallbacks"); ret = BID_SetInfoItem(handle, "useragent", "WGET 4.0 like Mozilla"); ret = BID_SetInfoItem(handle, "localeId", "nb"); ret = BID_SetInfoItem(handle, "sid", "SuperUniqueSessionID-1234"); ret = BID_SetInfoItem(handle, "timeout", "40000"); ret = BID_SetInfoItem(handle, "nexturl", "https://url.no/gohereafterwards"); ret = BID_SetInfoItem(handle, "merchantFEDomain", "url.no"); ret = BID_SetInfoItem(handle, "withCredentials", "Y"); ret = BID_InitSession(handle, &helperUri, &tid, &cid); /* The data is received, use the helperuri to load the Web-client (using the cid), *verify that the tid is correct in subsequent requests received from the Web-client, *allow the user to authenticate, sign, pay or change password. */ ret = BID_RemoveInfoItems(handle); BID_Free(helperUri); BID_Free(tid); BID_Free(cid);
Signing with web-client (BankID 2.1)
The code below shows an example of how the merchant initiates a transaction where the users uses the BankID 2.1 Web-client for signing.
char *helperUri = NULL; char *tid = NULL; char *cid = NULL; ret = BID_SetInfoItem(handle, "action", "sign"); ret = BID_SetInfoItem(handle, "merchanturl", "https://url.no/handleBankIDCallbacks"); ret = BID_SetInfoItem(handle, "useragent", "WGET 4.0 like Mozilla"); ret = BID_SetInfoItem(handle, "localeId", "nb"); ret = BID_SetInfoItem(handle, "sid", "SuperUniqueSessionID-1234"); ret = BID_SetInfoItem(handle, "timeout", "40000"); ret = BID_SetInfoItem(handle, "nexturl", "https://url.no/gohereafterwards"); ret = BID_SetInfoItem(handle, "docDisplayMode", "window"); ret = BID_SetInfoItem(handle, "merchantFEDomain", "url.no"); ret = BID_SetInfoItem(handle, "merchantFEAncestors", "domain1, domain2, domain3"); ret = BID_SetInfoItem(handle, "withCredentials", "Y"); ret = BID_SetInfoItem(handle, "showUnderstanding", "Y"); ret = BID_SetInfoItem(handle, "showConfirmation", "Y"); ret = BID_SetInfoItem(handle, "clientVersion", "2.1"); ret = BID_SetInfoItem(handle, "clientProxyURL", https://url.to/clientproxy); ret = BID_SetInfoItem(handle, "clientProxyPublicKey", "hexencoded modulus"); ret = BID_InitSession(handle, &helperUri, &tid, &cid); ret = BID_RemoveInfoItems(handle); BID_Free(helperUri); BID_Free(tid); BID_Free(cid);
BankID on mobile
The code below shows an example of how the merchant can initiate a bankid on mobile transaction.
char *transactionreference = NULL; char *merchantref = NULL; int retcode = 0; retcode = BID_GenerateMerchantReference(sessioncontext, "no_NO", &merchantref); retcode = BID_SetInfoItem(sessioncontext, "phonenumber", "12341234"); retcode = BID_SetInfoItem(sessioncontext, "phonealias", "010170"); retcode = BID_SetInfoItem(sessioncontext, "action", "auth"); retcode = BID_SetInfoItem(sessioncontext, "sid", "sessionid"); retcode = BID_SetInfoItem(sessioncontext, "merchantReference", merchantref); retcode = BID_RequestMobileAction(sessioncontext, &transactionreference); BID_RemoveInfoItems(sessioncontext); BID_Free(transactionreference); BID_Free(merchantref);
Authentication process
The code below shows an example of an authentication (login) use case.
char* encryptedresponse=NULL; char* serverchallenge=NULL; char* socialno = NULL; char* clientIp = NULL; /* When using the Web-client, the traceid must be set before calling the * BID_InitTransaction method. */ res = BID_SetInfoItem(sessioncontext,"traceid", "<traceid from initsession response>"); /* Authentication step 1 * Retrieve operation, encKey, encData and encAuth from client */ res = BID_InitTransaction(sessioncontext, encKey, encData, encAuth, operation, NULL, &encryptedresponse); res = BID_GetInfoItem(sessioncontext, "serverchallenge", &serverchallenge); /* Return encryptedresponse to client, clean up state data */ res = BID_RemoveInfoItems(sessioncontext); res = BID_Free(encryptedresponse); res = BID_Free(clientIp); res = BID_Free(serverChallenge); /* End of step 1 */ /* Authentication step 2 * Retrieve operation, encKey, encData and encAuth from client */ res = BID_SetInfoItem(sessioncontext, "serverchallenge", serverchallenge); /* When using the Web-client, the traceid must be set before calling the * BID_VerifyTransactionRequest method. /* res = BID_SetInfoItem(sessioncontext,"traceid", "<traceid from initsession response>"); /* To retrieve additional certificate information, add infoitems here. * Possible values: "addsocialno", "addaccountno" and "addorganisationno" */ res = BID_SetInfoItem(sessioncontext, "addsocialno", "true"); res = BID_VerifyTransactionRequest(sessioncontext, encKey, encData, encAuth, operation, NULL); /* Now it is possible to retrieve certificate information here */ res = BID_GetInfoItem(sessioncontext, "socialno", &socialno); res = BID_RemoveInfoItems(sessioncontext); /* An error code must be set if something went wrong * res = BID_SetInfoItem(sessioncontext, "errorCode", "some error code"); */ res = BID_SetInfoItem(sessioncontext,"nexturl","https://some URL"); res = BID_VerifyTransactionResponse(sessioncontext, &encryptedresponse ); /* Return encrypted response to client, clean up state data */ res = BID_RemoveInfoItems(sessioncontext); res = BID_Free(encryptedresponse); /* End of step 2 */
Signing process without SEID SDO
The below code shows an example of a signing use case where no SDO is created.
const char* contract = "I lease a Ford Focus from 01.11.2004-01.11.2005"; char* b64contract=NULL; char* signeddata=NULL; /* * Signing step 1 * Retrieve operation, encKey, encData and encAuth from client */ res = BID_Base64Encode(sessioncontext, contract, strlen(contract), &b64contract); res = BID_SetInfoItem(sessioncontext, "data", b64contract); res = BID_SetInfoItem(sessioncontext, "mimetype", "text/plain"); res = BID_SetInfoItem(sessioncontext, "datadescription", "Contract"); /* When using the Web-client, the traceid must be set before calling the * BID_InitTransaction method. /* res = BID_SetInfoItem(sessioncontext,"traceid", "<traceid from initsession response>"); res = BID_InitTransaction(sessioncontext, encKey, encData, encAuth, operation, NULL, &encryptedresponse); res = BID_GetInfoItem("signeddata", &signeddata); /* Return encrypted response to client */ res = BID_RemoveInfoItems(sessioncontext); res = BID_Free(encryptedresponse); res = BID_Free(b64contract); /* signing step 1 end */ /* * signing step 2 start * Retrieve operation, encKey, encData and encAuth from client */ res = BID_SetInfoItem(sessioncontext, "signeddata", signeddata); /* When using the Web-client, the traceid must be set before calling the * BID_VerifyTransactionRequest method. */ res = BID_SetInfoItem(sessioncontext,"traceid", "<traceid from initsession response>"); res = BID_VerifyTransactionRequest(sessioncontext, encKey, encData, encAuth, operation, NULL); res = BID_Free(signeddata); /* * An error code must be set if something went wrong * res = BID_SetInfoItem(sessioncontext, "errorCode", "some error code"); */ res = BID_SetInfoItem(sessioncontext, "nextUrl", "https://some url"); res = BID_VerifyTransactionResponse(sessioncontext, &encryptedresponse); /* Return encrypted response to client */ res = BID_RemoveInfoItems(sessioncontext); res = BID_Free(encryptedresponse);
Signing process with SEID SDO
The below code shows an example of a signing use-case where an SDO is created.
const char contract = "This is the contract"; char b64contract = NULL; char serverpkcs7 = NULL; char serverocsp = NULL; char clientpkcs7 = NULL; char clientocsp = NULL; char sdoxml = NULL; char b64sdoxml = NULL; BID_SEIDSDO seidsdo = NULL; char signeddata = NULL; char encryptedResponse = NULL; /* * Signing step 1 * Retrieve operation, encKey, encData and encAuth from client */ res = BID_Base64Encode(sessioncontext, contract, strlen(contract), &b64contract); res = BID_SetInfoItem(sessioncontext, "data", b64contract); res = BID_SetInfoItem(sessioncontext, "mimetype", "text/plain"); res = BID_SetInfoItem(sessioncontext, "datadescription", "Contract"); /* When using the Web-client, the traceid must be set before calling the * BID_InitTransaction method. */ res = BID_SetInfoItem(sessioncontext,"traceid", "<traceid from initsession response>"); res = BID_InitTransaction(sessioncontext, encKey, encData, encAuth, operation, NULL, &encryptedresponse); res = BID_GetInfoItem(sessioncontext, "signeddata", &signeddata); res = BID_GetInfoItem(sessioncontext, "pkcs7", &serverpkcs7); /* * Return the encryptedresponse to the client */ res = BID_RemoveInfoItems(sessioncontext); res = BID_Free(encryptedresponse); res = BID_Free(b64contract); /* * Signing step 2 * Retrieve operation, encKey, encData and encAuth from client */ res = BID_SetInfoItem(sessioncontext, "signeddata", signeddata); /* When using the Web-client, the traceid must be set before calling the * BID_VerifyTransactionRequest method. */ res = BID_SetInfoItem(sessioncontext,"traceid", "<traceid from initsession response>"); res = BID_VerifyTransactionRequest(sessioncontext, encKey, encData, encAuth, operation, NULL); res = BID_GetInfoItem(sessioncontext, "serverOcsp", &serverocsp); res = BID_GetInfoItem(sessioncontext, "clientPkcs7", &clientpkcs7); res = BID_GetInfoItem(sessioncontext, "clientOcsp", &clientocsp); res = BID_CreateSDO(sessioncontext, &seidsdo, signeddata, "text/plain", "a contract", clientPkcs7, serverPkcs7, clientocsp, serverocsp); res = BID_Free(signedData); res = BID_Free(serverpkcs7); res = BID_Free(serverocsp); res = BID_Free(clientpkcs7); res = BID_Free(clientocsp); res = BID_SDOToXMLEx(sessioncontext, seidsdo, &sdoxml); res = BID_SDOFree(sessioncontext, seidsdo); res = BID_Base64Encode(sessioncontext, sdoxml, strlen(sdoxml), &b64sdoxml); res = BID_Free(sdoxml); /* * Do not add the data to the sdo by calling BID_SDOAddData before the response is sent * back to the client. The client already has the transaction data. */ res = BID_SetInfoItem(sessioncontext,"sdo", b64sdoxml); res = BID_Free(b64sdoxml); /* * An error code must be set if something went wrong * res = BID_SetInfoItem(sessioncontext,"errorCode","someErrorCode"); */ res = BID_SetInfoItem(sessioncontext,"nextUrl","https://someUrl"); res = BID_VerifyTransactionResponse(sessioncontext, &encryptedresponse ); /* Return encrypted response to client */ res = BID_RemoveInfoItems(sessioncontext); res = BID_Free(encryptedresponse);
Signing process with Dynamic SEID SDO
The below code shows an example of a signing use-case where a Dynamic SDO is created.
BID_Signature signature; BID_Signature signature2; BID_Signature signature3; BID_SEIDSDO* seidsdo = NULL; char* b64contract = NULL; char* ocsp = NULL; /* * Step 1. * Retrieve 3 pkcs7 signatures with corresponding ocsp response via B2B or some * legacy system. Create a signature structure */ signature.pkcs7 = retrievedpkcs7_1; signature.ocsp = retrievedocsp_1; signature.next = &signature2; signature2.pkcs7 = retrievedpkcs7_2; signature2.ocsp = retrievedocsp_2; signature2.next = &signature3; signature3.pkcs7 = retrievedpkcs7_3; signature3.ocsp = retrievedocsp_3; /* * Step 2. Retrieve the contract that was signed. */ b64contract = retrievedcontract; /* * Step 3. Create the dynamic SDO. This SDO will not be sealed. * It will contain three signatures. */ res = BID_CreateDynamicSDO(sessioncontext, &seidsdo, b64contract, "text/plain", "a contract", &signature); /* * Step 4. Retrieve another pkcs#7 signature and ocsp response via B2B or some * legacy system. The retrieved signature shall be added to the created SDO. */ signature.pkcs7 = secondretrievedpkcs7; signature.ocsp = secondretrievedocsp; signature.next = NULL; res = BID_SDOAddSignature( sessioncontext, seidsdo, &signature b64contract); /* * Step 5. All signatures that shall be contained in the SDO are retrieved. * Now seal the SDO. */ res = BID_GetOwnCertStatus( sessioncontext, &ocsp); res = BID_SDOSeal(sessiocontext, sdo, ocsp, b64contract); /* * Step 6. Validate the final SDO. We require 4 signatures in the sdo and * demands that it is sealed. */ res = BIDDynamicSDOValidate( sessioncontext, sdo, b64contract, 4, 0);
BankID 2.1 Sign
The code below shows how to perform a BankID 2.1 sign operation with 2 documents.
/* init sign */ res = BID_AddDocumentText(sessioncontext, B64Encode("this is a small test"), "Description1"); res = BID_AddDocumentText(sessioncontext, B64Encode("this is a another test"), "descr2"); res = BID_SetInfoItem(sessioncontext, "traceId", traceid); res = BID_InitTransaction(sessioncontext, encKey, encData, encAuth, operation, NULL, &encryptedresponse); /* send response ... */ BID_Free(encryptedresponse); /* fetch signed data and signatures */ res = BID_GetSignedData(sessioncontext, 0, &signed_data, &signature); res = BID_GetSignedData(sessioncontext, 1, &signed_data2, &signature2); /* remove infoitems and stuff */ BID_RemoveInfoItems(sessioncontext); /* verify sign */ /* set data that should have been signed – the order must be the same as the order * the documents were added in! */ res = BID_SetSignedData(sessioncontext, signed_data); res = BID_SetSignedData(sessioncontext, signed_data2); res = BID_VerifyTransactionRequest(sessioncontext, encKey, encData, encAuth, operation, NULL); /* retrieve merchant ocsp */ res = BID_GetInfoItem(sessioncontext, "serverOcsp", &serverOcsp); /* retrieve signatures and ocsp */ res = BID_GetSignatureAndOCSP(sessioncontext, 0, &clientsignature, &clientocsp); res = BID_GetSignatureAndOCSP(sessioncontext, 0, &clientsignature2, &clientocsp2); /* retrieve rtReport */ res = BID_GetReportData(sessioncontext, "", &report);
PAdES serial signing of PDF documents
Note: It is not possible to mix serial and parallell signing in the same scenario.
In PAdES serial signing, the order in which the signatories sign the document matters (this is the "serial" part), and signatures and validation data is embedded in the document. The PAdES signed documents can additionally contain visual seals representing the signatures.
Generation of visual seals and generation of validation data can be done either by the COI or the merchant application. When the COI does it, it's referred to as "turnkey", while if the merchant application does it itself, it's referred to as "self-assembly/-ed/-er/-ing". Generation of seals and generation of validation data are independent, and all combinations of turnkey and self-assembly flows are supported, as detailed in this table, along with which functions must be called to enable each flow:
Turnkey seals | Self-assembled seals | |
---|---|---|
Turnkey validation | COI generates seals and validation data. Use | Merchant generates seals, COI generates validation data. Use |
Self-assembled validation | COI generates seals, merchant generates validation data. Use | Merchant generates seals and validation data. Use |
Signing process, serial - PAdES common functionality
Merchant must of course supply PDF data, but also hold intermediate data between requests from BankID webclient.
Code below shows one sketch for this.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Merchant needs a type to store the PDFs, document descriptions, and signing kind. Merchant must also hold intermediate // data between each request from the webclient, something like the documents_to_sign and signed_data_serial below. // struct DocsToSign { char *data; // bytes of pdf, base64 encoded char *description; BID_SerialSigningKind serialType; // BID_PARALLEL = 0, BID_SERIAL, BID_SERIAL_END_USER_ONLY } documents_to_sign[num_documents_to_sign]; BID_SerialSigningData *signed_data_serial[num_documents_to_sign] = {0}; // An array of pointers to BID_SerialSigningData ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // For all requests from BankID WebClient this function should be called to set correct formats and traceId int setTraceIdAndFormats() { int res = 0; if (res = BID_SetInfoItem(session, "traceID", "traceid from initSession response")) goto end; if (res = BID_SetInfoItem(session, "cmsFormat", "PKCS7_ISO320001")) goto end; if (res = BID_SetInfoItem(session, "ocspFormat", "OCSP_RFC6960_COMPATIBLE")) goto end; end: return res; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // It is the merchant's responsibility to hold state between requests from BankID WebClient int getIntermediateStateFromSession() { int res = 0; for(int i = 0; i < num_documents_to_sign; i++) { BID_SerialSignedData * ss = signed_data_serial + i; BID_FreeSerialSignedData(ss->serial_signing_data); ss->serial_signing_data = NULL; if (res = BID_GetSerialSignedData(session, i, &ss->serial_signing_data)) goto end; } end: return res; } int setIntermediateStateIntoSession() { int res = 0; // Insert state into BankID server, (BankID server takes a copy) for(int i = 0; i < num_documents_to_sign; i++) { if (res = BID_SetSerialSignedData(session, signed_data_serial[i].serial_signing_data)) goto end; } end: return res; }
Signing process, serial - PAdES, turnkey solution
The code below shows how to create a PAdES using the BankID turnkey solution. Missing parts are initializing the session and getting the data to sign, which are common with all other PDF signing using BankID.
Per-session data is supposed to just exist here, but must of course be handled by merchant.
BankID WebClient must be set to "2.1"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Turnkey solution, all documents are supposed to be PDF and serialType BID_SERIAL OR BID_SERIAL_END_USER_ONLY // Each document's mime type should be "application/pdf", and there should be no documents with serialType BID_PARALLEL here. if (strcmp(operation, "initSign") == 0) { if (res = setTraceIdAndFormats()) goto end; BID_VisualSealPosition merchantSealPos = { 10.0, 10.0, 1}; BID_VisualSealPosition endUserSealPos = { 100.0, 10.0, 1}; // Add documents to sign to BankID, must be at least one for(int i = 0; i < num_documents_to_sign; i++) { // You may also use NULL as value for seal positions, BankID will then use a default position. if (BID_AddDocumentPDFSerialSign(session, documents_to_sign[i].data, documents.to_sign[i].description, documents_to_sign[i].serialType, & merchantSealPos, & endUserSealPos)) goto end; } // Let the library know that we want serial signing, so it can detect it if // we mix up the document types. if ((res = BID_SetInfoItem("doSerialSigning", "Y"))) goto end; // initialize turnkey solution. If the second to last parameter is NULL, the turnkey flow will be used. if (res = BID_InitTransaction(session, encKey, encData, encAuth, operation, sid, NULL, &response )) goto end; // send the response to the client here BID_Free(response); // collect merchant signatures and signed data, needed for next request res = getIntermediateStateFromSession(); end: // clean up state BID_RemoveInfoItems(session); if (res) { // Do appropriate error handling for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } } } if (strcmp(operation, "initSignSignMerchantSeal") == 0) { // merchant seal has been generated in BankID ClientProxy but signing must be done at merchant site // BID server needs to sign the visual seals generated by BankID COI for merchant if (res = setTraceIdAndFormats()) goto end; if (res = setIntermediateStateIntoSession()) goto end; if (res = BID_InitSignSignMerchantSeal(session, encKey, encData, encAuth, operation, sid, &response)) goto end; // use the response - send it to the client here BID_Free(response); res = getIntermediateStateFromSession(); end: // clean up state BID_RemoveInfoItems(session); if (res) { // Do appropriate error handling for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } } } if (strcmp(operation, "verifySign") == 0) { if (res = setTraceIdAndFormats()) goto end; if (res = setIntermediateStateIntoSession()) goto end; // Inform bankid c server that now is a good time to do some magic. The last parameter is // for an optional BID_ValidationBuilder if the merchant application wants to do its own validation. // NULL means turnkey, i.e. Clientproxy will set OCSPs and Certs in PDF res = BID_VerifyTransactionRequest(session, encKey, encData, encAuth, operation, sid, NULL); // optionally fetch the report data here // prepare response to client if (res = BID_SetInfoItem(session, "nexturl", nexturl)) goto end; if (res = BID_VerifyTransactionResponse(session, &response)) goto end; res = getIntermediateStateFromSession(); // send the response to the client BID_Free(response); // cleanup end: BID_RemoveInfoItems(session); if (res) { // Do appropriate error handling for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Finally receiving validation data to complete the PAdES and thereafter extracting the resulting PAdES if (strcmp("verifySignAddDSS",operation) == 0) { char * resulting_pdfs_b64_encoded[num_documents_to_sign] = { 0 }; if (res = setTraceIdAndFormats()) goto end; if (res = setIntermediateStateIntoSession()) goto end; if (res = BID_VerifySignAddDSS(sessioncontext, enckey, encdata, encauth, operation, sid, &response)) goto end; // Get the complete PAdES'es for (int i = 0; i < num_documents_to_sign; ++i) { if (res = BID_GetSerialSignedPdf(session, i, &resulting_pdfs_b64_encoded[i])) goto end; } // send the response to the client BID_Free(response); // cleanup end: BID_RemoveInfoItems(session); // We are finished here, so everything may be cleaned up. // What to do with the resulting_pdfs is the merchant's responsibility. for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } }
Signing process, serial - PAdES, self assembling merchant solution
The code below shows how to create a PAdES using the BankID self assembling merchant. Missing parts are initializing the session and getting the data to sign, which are common with all other PDF signing using BankID.
Self assembling merchant must be capable of adding incremental updates containing visual seals to the PDF. The signing of these will be done by BankID server. Code for building these seals are not shown here.
BankID WebClient must be "2.1"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Self assembling merchant, all documents are supposed to be PDF and serialType BID_SERIAL OR BID_SERIAL_END_USER_ONLY // Each document's mime type should be "application/pdf", and there should be no documents with serialType parallel here. if (strcmp(operation, "initSign") == 0) { if (res = setTraceIdAndFormats()) goto end; BID_VisualSealPosition merchantSealPos = { 10.0, 10.0, 1}; BID_VisualSealPosition endUserSealPos = { 100.0, 10.0, 1}; for(int i = 0; i < num_documents_to_sign;i++) { // merchant places seal, but seal_position's page is used by Webclient to refresh that page when needed if (res = BID_AddDocumentPDFSerialSign(session, documents_to_sign[i].data, documents.to_sign[i].description, documents_to_sign[i].serialType, & merchantSealPos, & endUserSealPos)) goto end; } // Let the library know that we want serial signing, so it can detect it if // we mix up the document types. if ((res = BID_SetInfoItem("doSerialSigning", "Y"))) goto end; // The second-to-last argument, &addMerchantSeal, is a function that takes care of making the merchant seal. // Must be set even if all docs are BID_SERIAL_END_USER_ONLY. Must be non-NULL. if (res = BID_InitTransactionSelfAssembler(session, encKey, encData, encAuth, operation, sid, &addMerchantSeal, &response)) goto end; // use the response - send it to the client here BID_Free(response); // collect merchant signatures and signed data, needed for next request res = getIntermediateStateFromSession(); end: // clean up state BID_RemoveInfoItems(session); if (res) { // Do appropriate error handling for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } } } if (strcmp(operation, "initSignBuildEndUserSeal") == 0) { if (res = setTraceIdAndFormats()) goto end; if (res = setIntermediateStateIntoSession()) goto end; // BID server will callback to merchant to get the graphic and content for the endUser seals // This operation will be received once pr. document if (res = BID_InitSignBuildEndUserSeal(session, encKey, encData, encAuth, operation, sid, &addEndUserSeal, &response)) goto end; // use the response - send it to the client here BID_Free(response); // collect merchant signatures and signed data, needed for next request res = getIntermediateStateFromSession(); end: // clean up state BID_RemoveInfoItems(session); if (res) { // Do appropriate error handling for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // This is the last request from webclient. if (strcmp(operation, "verifySign") == 0) { char * resulting_pdfs_b64_encoded[num_documents_to_sign] = { 0 }; if (res = setTraceIdAndFormats()) goto end; if (res = setIntermediateStateIntoSession()) goto end; // BankID server will call the addValidationData function for each doc. Data added will be sent to client so // enduser may download the PDFs. // The last argument to this function is the function responsible for adding validation data to the PDF. // If non-NULL, self-assembling validation will be used. res = BID_VerifyTransactionRequestSelfAssembler(session, encKey, encData, encAuth, operation, sid, &addValidationData); // optionally fetch the report data here // prepare response to client if (res = BID_SetInfoItem(session, "nexturl", nexturl)) goto end; if (res = BID_VerifyTransactionResponse(session, &response)) goto end; // Get the complete PAdES'es for (int i = 0; i < num_documents_to_sign; ++i) { if (res = BID_GetSerialSignedPdf(session, i, &resulting_pdfs_b64_encoded[num_documents_to_sign])) goto end; } // send the response to the client BID_Free(response); // cleanup end: BID_RemoveInfoItems(session); if (res) { // Do appropriate error handling for (int i = 0; i < num_documents_to_sign; ++i) { BID_Free_SerialSigningData(signed_data_serial[i]); signed_data_serial[i] = NULL; } } } /** The addMerchantSeal and addEndUserSeal example callback function shall be implemented by a self assembling visual seal merchant. Purpose of methods are to add an incremental update to a pdf document. This incremental update is returned as a ByteRangeWithData structure. The pdfData is the plain pdf data as far as it has been processed, not B64 encoded. The pdfData may for example contain the merchant seal and signature if endUserSeal is asked for, but will contain the initial pdf bytes if the merchant seal is asked for. The certData holds several values from the certificate to create incremental update for. The resulting ByteRangeWithData may or may not contain the pdfData as prefix, that is the byterange returned must EITHER start at 0 and the corresponding data bytes must include the pdfData from index 0 up to datalen and the incremental update from then on OR start at datalen and include the incremental update from then on The incremental update must have a gap (described by the byteRange) where BankID server or BankID client will insert the signature over the original document added incremental update, gap removed. The gap must be large enough to hold the signature. BankID server inserts "<signaturedata hex encoded padded zeroes>" into the gap described by the ByteRange. See http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf page 476, about Contents BankID server takes ownership of the data returned, i.e. freeing memory in result will be handled by bankID server. */ int addMerchantSeal(int session, int docIndex, BID_ByteRangeWithData * pdfdata, BID_VisualSealCertData * certData, BID_ByteRangeWithData ** result) { * result = .... return 0; // if all ok } int addEndUserSeal(int session, int docIndex, BID_ByteRangeWithData * pdfdata, BID_VisualSealCertData * certData, BID_ByteRangeWithData ** result) { * result = .... return 0; // if all ok } /** The addValidationData example callback function shall be implemented by a self assembling validation data merchant. Normally BankID add these data into a DSS in the PDF, but merchants may do it and add further data as timestamps. The validation_result and it's data element will be freed by BIDCServer. The validation_result must either be an empty byterange with data in which case there will be no validation data in the PDF a byterange with data starting at 0 and including the pdfdata as first part a byterange with data starting at end of pdfdata containing the incremental update to add. ocsp and certs parameters are all Base64 encoded nullterminated strings of the DER encoding for the OCSPResponse or X509certificates. Certificate parameters are array of pointers to char, the last pointer is NULL, the array is never NULL. endUserX509Certs[0] is the endUserCert as B64, endUserX509Certs[1] is the issuer and so on. Normally endUserX509Certs[2] == NULL. Numbering is the same for the merchantX509Certs, but if the document is BID_SERIAL_END_USER_ONLY then merchantCerts[0] == NULL. */ int addValidationData(int sessioncontext, int docnum, BID_ByteRangeWithData * pdfdata, char * endUserFullOcspResponse, char ** endUserX509Certs, char * serverFullOcspResponse, char ** merchantX509Certs, BID_ByteRangeWithData ** validation_result) { * validation_result = bid_brwd_create_empty(); return 0; }
Retrieve certificate status from VA
The code below shows an example of how to retrieve the status of a certificate from VA.
char *ocspresponse = NULL; res = BID_GetCertStatus(sessioncontext, clientPkcs7, &ocspresponse); res = BID_RemoveInfoItems(sessioncontext); BID_Free(ocspresponse);
Retrieve additional information from VA
See [5.4] for the recommended way of retrieving additional information from VA.
The below code shows an example of how to retrieve the status of a certificate and at the same time retrieve additional information about the certificate holder.
/* * This code assumes that all three additional information items are available for the * requested certificate */ char* socialno=NULL; char* accountno=NULL; char* organisationno=NULL; ret = BID_SetInfoItem(sessioncontext, "addsocialno", "true"); ret = BID_SetInfoItem(sessioncontext, "addaccountno", "true"); ret = BID_SetInfoItem(sessioncontext, "addorganisationno", "true"); res = BID_GetCertStatus(sessioncontext, clientpkcs7, NULL); ret = BID_GetInfoItem(sessioncontext, "socialno", &socialno); ret = BID_GetInfoItem(sessioncontext, "accountno", &accountno); ret = BID_GetInfoItem(sessioncontext, "organisationno", &organisationno); /* * If BID_GetInfoItem doesn’t return the requested infoitem, the merchant might call * BID_GetInfoItem with the item concatenated with "err" to find the reason. * res = BID_GetInfoItem(sessioncontext, "socialnoerr", &socialnoerr); */ BID_Free(socialno); BID_Free(accountno); BID_Free(organisationno); BID_RemoveInfoItems(sessioncontext);
The infoitems addsocialno, addaccountno and addorganisationno must have value "true" to have any effect.
The BID_VerifyTransactionRequest method checks the certificate status, thus the BID_GetCertStatus method is no longer necessary to use if you use the BID_VerifyTransactionRequest method.
For multiple document signing the request for AI will be sent for the first document only. As described in the example above, the AI will be accessible to the merchant as infoitems.