Signing text or PDF documents - ASP.NET Core
This tutorial demonstrates how to you can easily add digital signature capabilities for PDF and plain text documents to your own application(s), by using Criipto Verify’s signing features.
Three steps are required to complete your first test login:
- Register application in Criipto Verify
- Set up your application to request PDF signature
- Set up your application to request plain text signature
The tutorial will walk you through the technical details of the integration process between your application and Criipto Verify, and will also explain how to use test e-ID’s to validate your integration. To use real e-ID’s for signing, the integration is the same, but you must be set up for Production.
This sample uses a .NET Core
backend and accompanying React.js
frontend. If you are building your system on other platforms, you can do exactly the same in any system that supports JSON
over HTTP
.
Register Your Application in Criipto Verify
After you signed up for Criipto Verify, you must register an application before you can try requesting a signature. If you gaven’t already done this, go to our management dashboard on manage.criipto.id, and navigate to the Applications
tab and create a new application.
The sample application for this tutorial uses the following settings. When you move outside the scope of the demo and start the integration from your own system, change the values accordingly so your system communicates with Criipto Verify using it’s dedicated settings.
Specifically, you will need the following information:
- Client ID to identify your application to Criipto Verify. In the case below we chose
urn:criipto:samples:no1
- Domain on which you will be communicating with Criipto Verify. Could be for example
samples.criipto.id
- Callback URL on which your application expects the result of the signing process from Criipto Verify
Register Callback URLs
Before you can start sending requests to Criipto Verify, you need to pre-register the URL(s) on which you expect to receive the returned JSON Web Token (aka a JWT
) on.
These URLs are known as Callback URL
s, and they are links to your system. Criipto Verify will send the JWT
containing the outcome of the document signing process only to a pre-registered Callback URL
.
Callback URLs for the sample project are: http://localhost:5000/sign/callback
and https://localhost:5001/sign/callback
:
Make sure to add your own Callback URL
s. Put each URL on a new line.
If you deploy your application to a different URL you will also need to ensure to add that URL to Callback URLs.
PDF document signatures
PDF document signatures are currently supported for Norwegian BankID only.
There are three important steps in obtaining a signature:
- Upload a PDF to Criipto Verify and retrieve a redirect URI
- Redirect the user to the redirect URI
- Handle a callback from Criipto Verify
1. Upload a PDF to Criipto Verify and retrieve a redirect URI
A first step in obtaining a signature is to upload one or more PDF documents to Criipto Verify, which will respond with a redirect URI where you have to redirect the users browser to, so they can complete the signature process.
Endpoint: /sign/pdfv1
Query parameters:
wa
- constant value:wsignin1.0
wtrealm
- Your Criipto Verify Client IDwreply
- a signature callback URL for your applicationwauth
-acr_value
of the authentication method. Currently only the Norwegian BankID,urn:grn:authn:no:bankid
, may be used for PDF signing.ui_locales
- specify the UI language to use by authentication provider
Body parameters:
- signProperties: Object<SignProperties>
- documents: List<PdfDocument>
SignProperties class:
orderName
- string displayed to the user as an instruction when starting the signature processshowConfirmation
andshowUnderstanding
- booleans that controls some aspect of the native provider’s UXpublic class SignProperties { public string orderName {get; set;} public bool showConfirmation {get; set;} public bool showUnderstanding {get; set;} }
PdfDocument class:
description
- string, per-document description displayed to the userpdf
- string, base64 encoded PDF documentseal
- Object<PdfSeal>, specify an absolute position of the seal in the signed documentpublic class PdfDocument { public string description {get; set;} public string pdf {get; set;} public PdfSeal seal {get; set;} }
PdfSeal class:
x
andy
- integer, absolute position of the sealpage
- number of the page where the seal will be applied topublic class PdfSeal { public Int64 x {get; set;} public Int64 y {get; set;} public Int64 page {get; set;} }
Full URI example: {your_criipto_domain}/sign/pdfv1?wa=wsignin1.0&wtrealm={your_criipto_id}&wreply=https://localhost:5001/sign/callback&wauth=urn:grn:authn:no:bankid&ui_locales=en
Body example:
var body = new PdfSignRequest {
signProperties = new SignProperties {
orderName = "Demo signing",
showConfirmation = true,
showUnderstanding = true
},
documents = new List<PdfDocument> {
new PdfDocument {
description = "Demo document",
pdf = "...base64-encoded PDF document contents (UTF8)...",
seal = new PdfSeal {
x = 10,
y = 10,
page = 1
}
}
}
};
If successful, Criipto Verify will respond with a JSON
literal with a redirectUri
property:
{
"redirectUri": "a URL that you must redirect the users browser to"
}
2. Redirect the users browser to the redirect URI
After you have successfully retrieved a redirect URI from Criipto Verify, you have to redirect the users browser to the redirect URI. The user will then be given the chance to review the document(s) and proceed to the actual signing.
3. Handle the callback from Criipto Verify
If signing was successful, a signature
property will be posted to the Callback URL
you specified in the first step, and it will contain a JWT.
Your system must now validate the JWT before consuming it for production purposes (such as storing it for bookkeeping purposes).
var validationParams = new TokenValidationParameters{
ValidIssuer = discoveryDoc.Issuer,
ValidAudience = _configuration["CriiptoVerify:ClientId"],
IssuerSigningKeys = discoveryDoc.SigningKeys,
};
var tokenHandler = new JwtSecurityTokenHandler{
InboundClaimTypeMap = new Dictionary<string, string>(),
MaximumTokenSizeInBytes = int.MaxValue
};
SecurityToken validatedToken = null;
var claimsPrincipal = tokenHandler.ValidateToken(response.signature, validationParams, out validatedToken);
var jwtToken = validatedToken as JwtSecurityToken;
if (jwtToken != null) {
ViewData["payload"] = jwtToken.RawPayload;
}
In the example above, jwtToken.RawPayload
will contain information about the user that signed the document, and a base64-encoded PDF with the seal. Below is an example of a jwtToken.RawPayload
retrieved from one of the test users.
{
"iss": "https://easyid.www.prove.id",
"aud": "urn:grn:app:easyid-signing-demo",
"sub": "{df8a73f0-8e54-472d-987d-0d218ae6a62e}",
"bankid_sub": "24ac2c8a-afe3-4e1e-ae49-87022adfccf2",
"birthdate": "1960-07-24",
"name": "Marko Bura",
"bankid_altsub": "9578-6000-4-454370",
"ssn": "24071462532",
"uniqueuserid": "9578-6000-4-454370",
"certissuer": "CN=BankID - TestBank1 - Bank CA 3,OU=123456789,O=TestBank1 AS,C=NO;OrginatorId=9980;OriginatorName=BINAS;OriginatorId=9980",
"certsubject": "CN=Bura\\, Marko,O=TestBank1 AS,C=NO,SERIALNUMBER=9578-6000-4-454370",
"evidence": [
{
"signedDocumentSha256": "pOIZryPECQB1nAtIfiw4Di7CFS5koXxrzDbbAMVKUKc=",
"padesSignedPdf": "{base64_encoded_signed_pdf}"
"description": "Demo document",
"unsignedDocumentSha256": "Avhz9KkWRKzVZ9iRC9lGk5hWiFjk6hMHvBH5/uS9hbs="
}
],
"iat": 1600524372,
"nbf": 1600524372,
"exp": 1600524372
}
A base64-encoded signed PDF can be retrieved from jwtToken.RawPayload.evidence[0].padesSignedPdf
.
Plain text signature
Plain text signature is supported for DK NemID, NO BankID and SE BankID.
There are two important steps in obtaining a signature:
1. Redirect the user to the signature endpoint
A first step is to construct a valid signature URL, and redirect the user to the Criipto Verify text signing endpoint: /sign/text
Query parameters:
wa
- constant value:wsignin1.0
wtrealm
- ˇYour Criipto Verify client IDwreply
- a signature callback URL for your applicationwauth
-acr_value
of the authentication method you want to use. You can find the exact value(s) to use heresigntext
- base64-encoded text to be signedorderName
- string displayed to the user as an instruction when starting a signature process by authentication providershowUnderstanding
andshowConfirmation
- booleans which control respective UI aspects of authentication providerui_locales
- specify the UI language to use by authentication provider
Full signature URL example: {your_criipto_domain}/sign/pdfv1?wa=wsignin1.0&wtrealm={your_criipto_id}&wreply=https://localhost:5001/sign/callback&wauth=urn:grn:authn:no:bankid&signtext=VGhpcyBpcyBhbiBleGFtcGxlLg%3D%3D&orderName=Signing%20demo&showUnderstanding=true&showConfirmation=true&ui_locales=en
If a valid signature URL has been constructed, Criipto Verify will redirect the user to the authentication provider and handle signing, after which the user will be redirected to the callback URL, and a JWT will be posted to the callback route.
3. Handle a callback from Criipto Verify
If signing was successful, a signature
property will be posted to the callback route you specified in the first step, and it will contain a JWT.
It’s recommended to validate a JWT before consuming it.
var validationParams = new TokenValidationParameters{
ValidIssuer = discoveryDoc.Issuer,
ValidAudience = _configuration["CriiptoVerify:ClientId"],
IssuerSigningKeys = discoveryDoc.SigningKeys,
};
var tokenHandler = new JwtSecurityTokenHandler{
InboundClaimTypeMap = new Dictionary<string, string>(),
MaximumTokenSizeInBytes = int.MaxValue
};
SecurityToken validatedToken = null;
var claimsPrincipal = tokenHandler.ValidateToken(response.signature, validationParams, out validatedToken);
var jwtToken = validatedToken as JwtSecurityToken;
if (jwtToken != null) {
ViewData["payload"] = jwtToken.RawPayload;
}
In the example above, jwtToken.RawPayload
will contain information about the user who signed the text. Below is an example of a jwtToken.RawPayload
retrieved from one of the test users.
{
"iss": "https://easyid.www.prove.id",
"aud": "urn:grn:app:easyid-signing-demo",
"signtext": "VGhpcyBpcyBleGFtcGxlLg==",
"sub": "{df8a73f0-8e54-472d-987d-0d218ae6a62e}",
"bankid_sub": "24ac2c8a-afe3-4e1e-ae49-87022adfccf2",
"birthdate": "1960-07-24",
"name": "Marko Bura",
"bankid_altsub": "9578-6000-4-454370",
"ssn": "24071462532",
"ocspResponse": "{ocsp_response}",
"uniqueuserid": "9578-6000-4-454370",
"certissuer": "CN=BankID - TestBank1 - Bank CA 3,OU=123456789,O=TestBank1 AS,C=NO;OrginatorId=9980;OriginatorName=BINAS;OriginatorId=9980",
"certsubject": "CN=Bura\\, Marko,O=TestBank1 AS,C=NO,SERIALNUMBER=9578-6000-4-454370",
"evidence": "{evidence}",
"iat": 1600528255,
"nbf": 1600528255,
"exp": 1600528255
}
Test users
Almost all e-ID types have a notion of test users and real users.
Real users are real people logging in to a web site, thus voluntering their real name and typically also a social security number, SSN.
Test users are either created by you for the occasion, or we provide you with access to already created test users.
You may ready more in the section on test users
Setting up for Production
Once you have integrated with Criipto Verify and tested that it works with test user accounts, you are ready to go to production to accept real e-ID logins and signatures.
Please note that for production usage a paid subscription is required.
Read more in the section on how to set up for production.
List of acr_values
Criipto Verify supports a range of country and bank specific e-ID services. They are all accessed through the same endpoints, e.g. https://<YOUR COMPANY>.criipto.id/oauth2/authorize
To pick the login method you must set the acr_values
parameter on the authentication request in order to choose the type of authentication you want. How you set this query string parameter varies with programming platform and your OpenID Connect library of choice.
The current list of possible values is:
Login method | acr_values |
---|---|
Norwegian BankID | |
Mobile or Web (user choice): | urn:grn:authn:no:bankid |
Norwegian Vipps Login | |
Login with Vipps app: | urn:grn:authn:no:vipps |
Swedish BankID | |
Same device: | urn:grn:authn:se:bankid:same-device |
Another device (aka mobile): | urn:grn:authn:se:bankid:another-device |
Danish NemID | |
Personal with code card: | urn:grn:authn:dk:nemid:poces |
Employee with code card: | urn:grn:authn:dk:nemid:moces |
Employee with code file: | urn:grn:authn:dk:nemid:moces:codefile |
Finish e-ID | |
BankID: | urn:grn:authn:fi:bank-id |
Mobile certificate (Mobiilivarmenne): | urn:grn:authn:fi:mobile-id |
Any of the two: | urn:grn:authn:fi:all |
Itsme | |
Basic: | urn:grn:authn:itsme:basic |
Advanced: | urn:grn:authn:itsme:advanced |
Belgium | |
Verified e-ID | urn:grn:authn:be:eid:verified |
Germany | |
Sofort (with Schufa check) | urn:grn:authn:de:sofort |