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 sealsSelf-assembled seals
Turnkey validation

COI generates seals and validation data.

Use BID_InitTransaction and BID_VerifyTransactionRequest.

Merchant generates seals, COI generates validation data.

Use BID_InitTransactionSelfAssembler and BID_VerifyTransactionRequest.

Self-assembled validation

COI generates seals, merchant generates validation data.

Use BID_InitTransaction and BID_VerifyTransactionRequestSelfAssembler.

Merchant generates seals and validation data.

Use BID_InitTransactionSelfAssembler and BID_VerifyTransactionRequestSelfAssembler.

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.