Skip to content

B2B-Bankid-Signer

Introduction

The B2B BankID Signer API is a RESTful API that allows you to sign documents using BankID merchant certificates. The API is designed for businesses that need to sign documents on behalf of their customers.

The merchant certificates is under the control of the BankID OIDC, an access token built from the BankID OIDC token endpoint with the scope "esign/b2b" is required to sign documents.

The API is versioned, and the current version is v0

The operations on the API comes in different flavours, parameters containing only hashes of the documents to be signed or parameters containing documents to be signed.

SDOs returned will for the document case contain the signed document, and for the hash case this document will of course be missing.

Validation of SDOs

The NETS SDO validator will be able to validate SDOs produced containing the document signed. Validation will fail if the document signed is not a part of the SDO.

The document itself is not part of the sealed or signed data in the SDO, only the hash of the document is. Therefore it is possible to add the document to the SDO after it has been signed.

To add the document to the SDO the following outline a solution (and the document should then validate in the NETS SDO validator):

public class Main {

    static String[] bytesSigned = "ueaouea".getBytes(StandardCharsets.ISO_8859_1);
    static String sdoWithoutDocumentB64 = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48U0RPTGlzdCB4bWxucz0iaHR0cDovL3d3dy5ucHQubm8vc2VpZC94bWxza2plbWEvU0RPX3YxLjAiIHhtbG5zOlhBZEVTPSJodHRwOi8vdXJpLmV0c2kub3JnLzAxOTAzL3YxLjIuMiMiIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj48U0RPPjxTRUlEU0RPVmVyc2lvbj4xLjA8L1NFSURTRE9WZXJzaW9uPjxTRE9EYXRhUGFydD48U2lnbmF0dXJlRWxlbWVudD48Q01TU2lnbmF0dXJlRWxlbWVudD48U0RPUHJvZmlsZT5TRUlELVNETy1CYXNpYy1WPC9TRE9Qcm9maWxlPjxTaWduYXR1cmVQb2xpY3lJZGVudGlmaWVyPjxTaWduYXR1cmVQb2xpY3lJZD48U2lnUG9saWN5SWQ+PFhBZEVTOklkZW50aWZpZXI+dXJuOm9pZDoyLjE2LjU3OC4xLjE2LjQuMTwvWEFkRVM6SWRlbnRpZmllcj48L1NpZ1BvbGljeUlkPjwvU2lnbmF0dXJlUG9saWN5SWQ+PC9TaWduYXR1cmVQb2xpY3lJZGVudGlmaWVyPjxTaWduZXJzRG9jdW1lbnRGb3JtYXQ+PE1pbWVUeXBlPnRleHQvcGxhaW48L01pbWVUeXBlPjwvU2lnbmVyc0RvY3VtZW50Rm9ybWF0PjxIYXNoZWREYXRhPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJTSEEtMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPnNlWkVEWWJyMkhEN0RjaVU4QVFlZjltTHB0Ky9nOWUxTFloL0J0S2lDTGM9PC9kczpEaWdlc3RWYWx1ZT48L0hhc2hlZERhdGE+PENNU1NpZ25hdHVyZT5NSUlPQkFZSktvWklodmNOQVFjQ29JSU45VENDRGZFQ0FRRXhEVEFMQmdsZ2hrZ0JaUU1FQWdFd0N3WUpLb1pJaHZjTkFRY0JvSUlMaHpDQ0JSa3dnZ01Cb0FNQ0FRSUNBeUhnNERBTkJna3Foa2lHOXcwQkFRc0ZBREJoTVFzd0NRWURWUVFHRXdKT1R6RVZNQk1HQTFVRUNnd01WR1Z6ZEVKaGJtc3hJRUZUTVJJd0VBWURWUVFMREFreE1qTTBOVFkzT0RreEp6QWxCZ05WQkFNTUhrSmhibXRKUkNBdElGUmxjM1JDWVc1ck1TQXRJRUpoYm1zZ1EwRWdNekFlRncweU1qQTFNVEl4TURRNE1UUmFGdzB5TmpBMU1USXhNRFE0TVRSYU1Ha3hHREFXQmdOVkJHRU1EMDVVVWs1UExUYzNOemMzTnpjM056RUxNQWtHQTFVRUJoTUNUazh4RlRBVEJnTlZCQW9NREVKaGJtdEpSQ0JVYjI5c2N6RVNNQkFHQTFVRUJSTUpOemMzTnpjM056YzNNUlV3RXdZRFZRUUREQXhDWVc1clNVUWdWRzl2YkhNd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUM3QnBPUGdMc2F1NVBObWZxblptMmtJNzZVRmVPN1NpWjUzZDAzdkgyakU1ZzFsYi9SdE83bnJiTmZ4MnZuSEloM1RITFJBRzc4QzVPNWUrZ2lMV3dnSjhDeDF2SVBZVUtOTFh1MXJUdHBoUm03M1F4M3RqWTBIOUJQRU1Fb3dPY3lNcEhYb3pjUnpUWC9zWmIrb2dwM1ZKSzZEcHVEbFFxdjVXOHpkWU1oY2dkOXJ5OGtFWlRhWC8wbVVlWjlzV2ZpTm1xczlKaTREM0dyK24zS3RqN3g0eDdWaXpaYzZ2RjJZemJzS3pQVDJ3T0cweFJaZFlXbU5XcHBFai9GelBoR21qeGRUb1ZObTJHTlZjR2pyUUYzTHNmSDlDWHRlbkxWT3c2VTRVVVlkc3JON2lENmpackNkaEpwdm9pL3lLQTYzNDN4MWVQSHBrb2RMVkRtUmJ3cEFnTUJBQUdqZ2RFd2djNHdFd1lIWUlSQ0FSQUNBUVFJTUFZRUJEazVPREF3RkFZSFlJUkNBUkFDQWdRSk1BY0VCVUpKVGtGVE1Ea0dDQ3NHQVFVRkJ3RUJCQzB3S3pBcEJnZ3JCZ0VGQlFjd0FZWWRhSFIwY0hNNkx5OTJZUzF3Y21Wd2NtOWtNUzVpWVc1cmFXUXVibTh3RmdZRFZSMGdCQTh3RFRBTEJnbGdoRUlCRUFFR0FRRXdEZ1lEVlIwUEFRSC9CQVFEQWdaQU1COEdBMVVkSXdRWU1CYUFGQUZwa3lDY0dEVUZPeHptNXN5elBDeXBXb1h1TUIwR0ExVWREZ1FXQkJUNHY4TnFWdThpc3o4dG5XYUJWUTJaUG9SVytEQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FnRUFJc0x4UGtTYkhXVGpPclFkbjd4TXorRVgrbHRnaDZDUXVyV3lyZ2ZySDNOaVBhOGdrZzNMQlhTck8zWTE5aEZnaGZmRnNWMzBiR2dTeWxSM2IxbnVCUzAzU0FQTEhCMjBiQ09SNXo0SEgwU0VzbVRpYTBqWDBxQTlhZm5JSTUvUFdnSWZKcmNYMUdZQkZwa3Q5ZEM0WSt2aU9hSmkvZ3R1R1dkR3IwS1FpWDhUQU1iS1o5ZUZyTUlOOFczMVFMM2oyMkN1TlliVzVNVkJ1OEozVERLM1RENHUvVWVxRjlOVWRsRW5xWXpxaHNHNEdsRGdYWldMTVFST1dZYUhGQkJZb091cDRjNkJPN1JDMnVkczMvZmMxc1Fpc21waGlmQlpFTVBHZFE5UVc1RVFQV1FsSC9Td0pqREQ5Q2ErM1NCeGJGL2pjVENqdnhEMGJ5bE9NejhSbHBQY2NZdUtZNklLUzZCRVljZEFicGlWK1g0U2FMU05FZnJQaVdadHRIVGdQR2MxR2wzblFqQVFnMHA5R1lndkZ3bEQrU0FmSUtlN01ZR1pPdUhHa0xkNXhyQko5cXNCbHJFdC9HcUgvZHdGQzY4NVZUcFg0RlpIL3gwNUVXbXhvMnY3Ym5JcURhTjVlUUxRRnNhdWNEcXJoZDVoQklVOFoxcTRlNEtQL1o5Mi80eElwM0YzNG1pcXkyVEk4NUM3SWplTkZkanBwS05IMXRjcUN1QzU3WFRNNTJIdjBOR0dsMHBoOTZnQngxaXlFdTBMajJrOGc4N2p0UzVCMEN6U0NZVEVDL2o4VXE3bXEzdjUrNXBINmRTeGNyYTlJWFBZNGF1K1dZWTFubjNkM3ZWZkdmYklsWjIyemZXOUs0M1FyZHdkRDIwK1ErUDBLcXlsNC8rTVp1b3dnZ1ptTUlJRVRxQURBZ0VDQWdJRjN6QU5CZ2txaGtpRzl3MEJBUXNGQURCeE1Rc3dDUVlEVlFRR0V3Sk9UekVxTUNnR0ExVUVDZ3doVkVWVFZDQXRJRVpPU0NCdlp5QlRjR0Z5WldKaGJtdG1iM0psYm1sdVoyVnVNUll3RkFZRFZRUUxEQTFVUlZOVUlDMGdRbUZ1YTBsRU1SNHdIQVlEVlFRRERCVlVSVk5VSUMwZ1FtRnVhMGxFSUZKdmIzUWdRMEV3SGhjTk1UZ3hNVEl3TURreU5qUXlXaGNOTXpBeE1USXdNRGt5TmpReVdqQmhNUXN3Q1FZRFZRUUdFd0pPVHpFVk1CTUdBMVVFQ2d3TVZHVnpkRUpoYm1zeElFRlRNUkl3RUFZRFZRUUxEQWt4TWpNME5UWTNPRGt4SnpBbEJnTlZCQU1NSGtKaGJtdEpSQ0F0SUZSbGMzUkNZVzVyTVNBdElFSmhibXNnUTBFZ016Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU9MQko5ekVUYUpHQUpmaDMvWnprNVhwWHVpelhaRTYwZVVRa0x4S2VwdlJtSGhzbVFKQ3prV0NlK0R0ckNnbktwV2FRbFV6OVh2Qlh3SlpoQ3JpOFJHMGcrU1RPODFyNnNiYkN0SmxCZGV0VGZpdjY5Z3krRWx3T1ExRFFuR1FuaDc5eWJZM3doYjJxMEc3WW51R0pvOERBalVHMUVYb3hqS0pacXhjdWNSMUZIelpiUEowL05SQTdlYXV2N3E3N2NLZjFnRlY0bnU1L3lNcEtrNHBIaGRqNVIwdHc2d3B0V0sxQjVhU1VadzYzaHdVd0tYSXY1andRaDkxOXFFd1krenlqL2hGSTVDdUxaQWdtT1RMeXBVb1dybjR0MHdDeVErMTFYRlpaQWRpa2lHdjE1WXUrZXV3MFFraitIMHltQVNzaCtTTkl2NGVRallhV2d6TEJkQnZEMUZ5WW1zOExSNThaUHhNQUNvT240emZTQW5IM3gxejhib3UxM2grOG85WUxkeVVLWkVUZkkrZnUrSzFlWUp2RDJ5TExoWGhlRkRXZDlMcGFxdjdNdHlFYzRrUkdPTXR1cXVVMmk1SDRTeU9jbHcrVFlkK0d3cDBFcisxak85RnkzSUdWU3J6dDVpTlBIQTdkVlJTajkrVlhwWXJBNmw2Z1Z2cThMQTBaL2FhZ0hRelV6KzU2QnpqeVRKa0JlMDVsdTJsYkNNQTRQNEs4MkVTVXA2Y1hKZi94NlgxSFhxU1pzUU9hVld0WUNFck1Ha2hXUzNYOEh2ZDl2WDhsSjBYazNuVytDZ01HdmpmMzZ6VDUzbTVTbFZneW8wT3ZIVVh2WXlUQ1R0UjJMUlczZ2RhTWd5RkVib3N5TmdyNHowOTlteldlQUJseTBGK0hOYk1JckYvQWdNQkFBR2pnZ0VXTUlJQkVqQVNCZ05WSFJNQkFmOEVDREFHQVFIL0FnRUFNQlVHQTFVZElBUU9NQXd3Q2dZSVlJUkNBUkFCQXdFd1R3WUlLd1lCQlFVSEFRRUVRekJCTUQ4R0NDc0dBUVVGQnpBQ2hqTm9kSFJ3Y3pvdkwyTnlkQzF3Y21Wd2NtOWtMbUpoYm10cFpDNXVieTkwWlhOMExXSmhibXRwWkhKdmIzUmpZUzVqY25Rd0RnWURWUjBQQVFIL0JBUURBZ0VHTUI4R0ExVWRJd1FZTUJhQUZOTkdWeXQzMG1MdmE4d3p4eHhjZmpPVktxSWVNRVFHQTFVZEh3UTlNRHN3T2FBM29EV0dNMmgwZEhCek9pOHZZM0pzTFhCeVpYQnliMlF1WW1GdWEybGtMbTV2TDNSbGMzUXRZbUZ1YTJsa2NtOXZkR05oTG1OeWJEQWRCZ05WSFE0RUZnUVVBV21USUp3WU5RVTdIT2JtekxNOExLbGFoZTR3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCQUdZRUkvWi9uNnpOM3VoNXIwaW9JUkpFbno3amZaVlhyNWpQcit5Sys5VDFDZUhMN3VOWHA4dGxMUCtnVm9IK2h1aWw3TDh1cW5TR2c0cTBYZFVHZUhyTEZQUlhqSEdTSVRxQzZiZ2puamFMcWdwSDN2cVdLTGtFYlE3M21QcDhWQmY1ZE1kNVRNYnhFaVVuenBvZkRZdFFGWGFnVGN5WUlkMGlUbG5xeTF0cS9aR2FGUE5mUXRZUlFVQkZUMzRVWE9Qc212U2d1NnhDUk40a2hYZTViKzQzSnlySTRidGlrcktNTlEwTmdhMkhoNFByd1Z3a2xCNWllWTdPS2lUSE8yWTBUczYrTFVoa2l0dzlQV0pPTTNoOXpTMXZCQlNkSzYxNGNtakN6TG4zQXA5a1VsM3BCUEVEMHdsUlFpbjdJYWpYWGZuN3pWQ0VCeVFXWDh4TFROVUNqNHdJZlBqam55YzVPQnJOcUI1aU82VGxTUHdHZ0FWNi84aTRFd0NZTXJkamVkOFA0M01LRWY5Vlh5R1FoZkMxOFhQQVNZam9sSERQaHBsVVljWUJYNlZYUlYyeWFMTGx4cUF3Zy9YUkZURENZMUpRMnY2V25JbjNIMGpBWU9OZ0VTYWY0bWRpc2l0cUp3VWtrN3A5Y3kwTkJQNkQ1NUV2c3FHenhtQ0Y5dEpOdDBCNUc1anVBZGVSNjVpa29qTjBzY3I3RmFxL1llMEJCeW1GSFVBcEkxZVBRQlBmM09MMGo1eVFkKy9WVWxvYlhrTlExcTEwK1dHQkFmaUN2eVc1WklERHkyTTNPUlRta3h0ei9VblN0U05CTGVvUW5DU01CUUxvSHo3cnBnSnE0YzBQSnNoa0FOa0dGUEtVOXJsMW9BblU5UngxUVVQazBiUkd3SS9HTVlJQ1F6Q0NBajhDQVFFd2FEQmhNUXN3Q1FZRFZRUUdFd0pPVHpFVk1CTUdBMVVFQ2d3TVZHVnpkRUpoYm1zeElFRlRNUkl3RUFZRFZRUUxEQWt4TWpNME5UWTNPRGt4SnpBbEJnTlZCQU1NSGtKaGJtdEpSQ0F0SUZSbGMzUkNZVzVyTVNBdElFSmhibXNnUTBFZ013SURJZURnTUFzR0NXQ0dTQUZsQXdRQ0FhQ0JzVEFZQmdrcWhraUc5dzBCQ1FNeEN3WUpLb1pJaHZjTkFRY0JNQndHQ1NxR1NJYjNEUUVKQlRFUEZ3MHlOVEF5TWpjeE1USXdOVGxhTUM4R0NTcUdTSWIzRFFFSkJERWlCQ0N4NWtRTmh1dlljUHNOeUpUd0JCNS8yWXVtMzcrRDE3VXRpSDhHMHFJSXR6QkdCZ3NxaGtpRzl3MEJDUkFDRXpFM01EVXdNekF4TUM4d0N3WUpZSVpJQVdVREJBSUJCQ0N5WXMvdHp5UGw2SkFiM25xLzB3dE4yV3g0UndBQ0Fva2JVRFlJaS81aFZUQUxCZ2txaGtpRzl3MEJBUUVFZ2dFQW95QWpvbHFkUUcwcW8wbmZOUXVXNTVYYkg0d1kyK3EvTHYrQzQ4N01yaFkxYnNEOHlqcmZzcjZLS0FNdkdoQkNCUXBFTlpXTGlOYlRCWGVNcHkyUWhYekZwb1dUdjNnUnl6eDRPNkUrcWtQdXlzL05VWVdVUkJwS0NoMHlCeDMzK0FCMjQzVCtXMUNKTW41N1dLSnJhWEJzSDVKVmNFRFNTZFFJT0lkNEpiYzJQNjJ0dWhBSXQxMkc5N1VoZGtpRm85Wk5ZSkkxa0RrT3o1Y2habkdNYktoWUpnSVZwWFp5NHRQaVJZcm03Ly8zZEJROHhXRVdFRVVpSEVhdURZYU5QcGJtUVZXbjBuTDVBMVY0R1FUNG5SMmo3WE82eVBJdTRIekdGeDhTSGkwRFJEY3BLN292TkV3MEZFUjJXMUFNWXQrZU5iZkIrcWVMcFU0N1NIL29xZz09PC9DTVNTaWduYXR1cmU+PFVzZXJDZXJ0aWZpY2F0ZUFuZFJldm9jYXRpb25EYXRhPjxSZXZvY2F0aW9uVmFsdWVzPjxYQWRFUzpPQ1NQVmFsdWVzPjxYQWRFUzpFbmNhcHN1bGF0ZWRPQ1NQVmFsdWU+TUlJR3d6Q0I1cUZSTUU4eEN6QUpCZ05WQkFZVEFrNVBNUmN3RlFZRFZRUUtEQTVVUlZOVUlFSmhibXRKUkNCQlV6RU5NQXNHQTFVRUN3d0VWRVZUVkRFWU1CWUdBMVVFQXd3UFZHVnpkQ0JDWVc1clNVUWdWa0V6R0E4eU1ESTFNREl5TnpFeE1qRXdNRm93VVRCUE1Eb3dCd1lGS3c0REFob0VGRExCZFBCczNSaHFFd0w5cHk5WjIwYTJaV0x0QkJRQmFaTWduQmcxQlRzYzV1Yk1zendzcVZxRjdnSURJZURnZ0FBWUR6SXdNalV3TWpJM01URXlNVEF3V3FFdE1Dc3dLUVlKS3dZQkJRVUhNQUVDQkJ4blNVZHpWMDVpZUZaRFpFeHpORzlZVWlzd2RGcExXblpUTTI4OU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ3JYS3M1amxrUFNSekR0VlJxZ1BrbnU1anR4N3JFMWxBTDgwNTZPNkx4Vnp2VUp1TDlVaU9yb3BhRGR0bDdMeUExWnhXTWU2ZjYyVmlKY1ZtN0RRQ0prR2tkOFFoN2gzdTVSdnM4T2gxNHdBZnNmQzFjQlh4NFpiOHVkVnJqMDcwSnJwVWVYcjJ1K1hnMWo5cmhxNi8yTXBQMEI0Tk9yVndUYUw4ZVg3K1JaY3dPZ2prakdvMVF5RTFRZGZWWTRBOU93dUFzMVRGZ0N3NmZUVitPUVJ3TFppczltYUFkVFJ0QXArb09Ed2t2YTF6dEdtbWZHMlVNYy90RUNKWnZRNENqT1BvTWdWenFFaS8wRVpMUVZwdzU1WTN3WFZHOWg4TmxPektaakhaNjhvazN5R1N0N0lVREJ3Q0RYY3J1a2hRbkdra2RydEhveHhqTE1BbzNnVEpZb0lJRXdqQ0NCTDR3Z2dTNk1JSUNvcUFEQWdFQ0FnSUpDVEFOQmdrcWhraUc5dzBCQVFzRkFEQnhNUXN3Q1FZRFZRUUdFd0pPVHpFcU1DZ0dBMVVFQ2d3aFZFVlRWQ0F0SUVaT1NDQnZaeUJUY0dGeVpXSmhibXRtYjNKbGJtbHVaMlZ1TVJZd0ZBWURWUVFMREExVVJWTlVJQzBnUW1GdWEwbEVNUjR3SEFZRFZRUUREQlZVUlZOVUlDMGdRbUZ1YTBsRUlGSnZiM1FnUTBFd0hoY05Nak13TVRFd01UUXlORFU1V2hjTk1qY3dNVEV3TVRReU5EVTVXakJQTVFzd0NRWURWUVFHRXdKT1R6RVhNQlVHQTFVRUNnd09WRVZUVkNCQ1lXNXJTVVFnUVZNeERUQUxCZ05WQkFzTUJGUkZVMVF4R0RBV0JnTlZCQU1NRDFSbGMzUWdRbUZ1YTBsRUlGWkJNekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFPTkhGZS90UXFDdlhmOHpwMGJTOVNjVkl6K3hiZlNsWEdhSkxFMlRkSE83OVQ1Mm5PSld2Nlk0M3ZDcHZraG5LaEc3NmNELzZyYjJra1VzVUVxalEwN0pTQkhWMytqS2FmUjZhWUpzaGZmQ3NYZEp4YWFlVFNZcXlNQjhHQ0E5UCswUjQwOFpGQlI1QUs4akR4WnlvdWRJcitHanprdmxtaWdJenpuRERVU1ZWRlFPZkJnQjB0Qm51RHpkeXhGbDdVVy9wdjRVK01yTjR4YTE2Y3EwZVowOTFoSGlkcEJxZjhOWVgyditUMktzelM3N0Fha3M2TW9HZjNyOTN0UmJLV0VJMEpRYnM4REk1Zkw0Wm5wajdSMU95UDh4OHZ6RjhmdkVmQ0UyR3Y4MThLTnlKUktROVcyUmRDNlYrQjZ3aVAyMi9MYmJQQUNtaEVocmg1Ynd4ak1DQXdFQUFhTitNSHd3RlFZRFZSMGdCQTR3RERBS0JnaGdoRUlCRUFFRkFUQU9CZ05WSFE4QkFmOEVCQU1DQnNBd0V3WURWUjBsQkF3d0NnWUlLd1lCQlFVSEF3a3dId1lEVlIwakJCZ3dGb0FVMDBaWEszZlNZdTlyekRQSEhGeCtNNVVxb2g0d0hRWURWUjBPQkJZRUZKSlB0OHh3cGVCUDlGc0V1SGtORlptVUsycHZNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUJQNTZNVS9RNXNQQ2RwOEtzL2pWZG1sbHlET1hYVkMzWVBFdG90QUZNZEVpR29GUHBuMzJoamNJSXo1QVBNaGc2VFh3RXo3UGR5ZmNtSmswUFdNbGxDdld2eERqeXNZQVlTRDUwa2l2bitpRmdLczdDcjEwME81QW4wMm8yaXd3dndQVnJPZ0dWdmlSRFJNWGpQeVlTdzQ5MXM4SjNucmhCOXVYZE15Qmxyb0EzT3RHU3lZTi9PSlJJQTVUcS9iUHc1SFVjdGQyU0FhdDlUNFRyd3dmRVlxbExMcHJGZElEOVRFbjFWaXVBZWZrcGd5T21lTTIrblg3SVBxcG5lUFNCKy9tN2dMMGl4a3JNdFBDaGNjSnQvU0tMSXE5UTJoZDBUYjlDTi9qVjcvYUF2Q0pWdTlsT3hwcTdCSVNBVTlzOTcvdE9SNVlHdkNOWW5hYmtJWDhqVkRWa1ZmOFY2bG9rdURuSVFiY1hFc3l6RXJjZldnRUpBU3ZxR3FsNnRJWm1wN3RZbkpaM2NDT0JtSEJoSGlWbThVOXlVWE50cXpYck0yQWNPOWhOeFZZSTNFUUR0d1NxOHpXeG95cHJyNDE2eUJQOFE1dzRlbGpEbXBoZzJjd0F6cVl5dFZuZWpKSjNGbkJseVZPTFJRYjlsNjVZd3Z6T0NrdmNBakJFM2pWTS9JOEV4eVhoMElhVTNZZWRhYWk5Y3FWVXc2NEc0TmE5TDFTaGlxWVNFUTFseEUrRmVyVTE5V3dESjB1TklzcmNpV0Eyc3MvT0NBY1NNZ3RwOE8yU0t6MjhYbGVFYmcvVUtoaDl5cjZLcEJYaGw4S25nWmNFR2NGckdkSTlqVXYyU0hYUUI0b3FrQ1RKNXVQTHk5RWthR0FkVkdXUWY2ZzlpYjdZei9vSkErQT09PC9YQWRFUzpFbmNhcHN1bGF0ZWRPQ1NQVmFsdWU+PC9YQWRFUzpPQ1NQVmFsdWVzPjwvUmV2b2NhdGlvblZhbHVlcz48L1VzZXJDZXJ0aWZpY2F0ZUFuZFJldm9jYXRpb25EYXRhPjwvQ01TU2lnbmF0dXJlRWxlbWVudD48L1NpZ25hdHVyZUVsZW1lbnQ+PC9TRE9EYXRhUGFydD48U0RPU2VhbD48U0RPU2lnbmF0dXJlPjxDTVNTaWduYXR1cmVFbGVtZW50PjxTRE9Qcm9maWxlPlNFSUQtU0RPLUJhc2ljLVY8L1NET1Byb2ZpbGU+PFNpZ25hdHVyZVBvbGljeUlkZW50aWZpZXI+PFNpZ25hdHVyZVBvbGljeUlkPjxTaWdQb2xpY3lJZD48WEFkRVM6SWRlbnRpZmllcj51cm46b2lkOjIuMTYuNTc4LjEuMTYuNC4xPC9YQWRFUzpJZGVudGlmaWVyPjwvU2lnUG9saWN5SWQ+PC9TaWduYXR1cmVQb2xpY3lJZD48L1NpZ25hdHVyZVBvbGljeUlkZW50aWZpZXI+PFNpZ25lcnNEb2N1bWVudEZvcm1hdD48TWltZVR5cGU+dGV4dC9wbGFpbjwvTWltZVR5cGU+PC9TaWduZXJzRG9jdW1lbnRGb3JtYXQ+PEhhc2hlZERhdGE+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09IlNIQS0yNTYiLz48ZHM6RGlnZXN0VmFsdWU+TFF6dmpWMW4rOXdpdTNQQXlGYTcxWU5aN21qa2VoNno1ck9GUlBsRkxsZz08L2RzOkRpZ2VzdFZhbHVlPjwvSGFzaGVkRGF0YT48Q01TU2lnbmF0dXJlPk1JSU9CQVlKS29aSWh2Y05BUWNDb0lJTjlUQ0NEZkVDQVFFeERUQUxCZ2xnaGtnQlpRTUVBZ0V3Q3dZSktvWklodmNOQVFjQm9JSUxoekNDQlJrd2dnTUJvQU1DQVFJQ0F5SGc0REFOQmdrcWhraUc5dzBCQVFzRkFEQmhNUXN3Q1FZRFZRUUdFd0pPVHpFVk1CTUdBMVVFQ2d3TVZHVnpkRUpoYm1zeElFRlRNUkl3RUFZRFZRUUxEQWt4TWpNME5UWTNPRGt4SnpBbEJnTlZCQU1NSGtKaGJtdEpSQ0F0SUZSbGMzUkNZVzVyTVNBdElFSmhibXNnUTBFZ016QWVGdzB5TWpBMU1USXhNRFE0TVRSYUZ3MHlOakExTVRJeE1EUTRNVFJhTUdreEdEQVdCZ05WQkdFTUQwNVVVazVQTFRjM056YzNOemMzTnpFTE1Ba0dBMVVFQmhNQ1RrOHhGVEFUQmdOVkJBb01ERUpoYm10SlJDQlViMjlzY3pFU01CQUdBMVVFQlJNSk56YzNOemMzTnpjM01SVXdFd1lEVlFRRERBeENZVzVyU1VRZ1ZHOXZiSE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzdCcE9QZ0xzYXU1UE5tZnFuWm0ya0k3NlVGZU83U2laNTNkMDN2SDJqRTVnMWxiL1J0TzducmJOZngydm5ISWgzVEhMUkFHNzhDNU81ZStnaUxXd2dKOEN4MXZJUFlVS05MWHUxclR0cGhSbTczUXgzdGpZMEg5QlBFTUVvd09jeU1wSFhvemNSelRYL3NaYitvZ3AzVkpLNkRwdURsUXF2NVc4emRZTWhjZ2Q5cnk4a0VaVGFYLzBtVWVaOXNXZmlObXFzOUppNEQzR3IrbjNLdGo3eDR4N1ZpelpjNnZGMll6YnNLelBUMndPRzB4UlpkWVdtTldwcEVqL0Z6UGhHbWp4ZFRvVk5tMkdOVmNHanJRRjNMc2ZIOUNYdGVuTFZPdzZVNFVVWWRzck43aUQ2alpyQ2RoSnB2b2kveUtBNjM0M3gxZVBIcGtvZExWRG1SYndwQWdNQkFBR2pnZEV3Z2M0d0V3WUhZSVJDQVJBQ0FRUUlNQVlFQkRrNU9EQXdGQVlIWUlSQ0FSQUNBZ1FKTUFjRUJVSkpUa0ZUTURrR0NDc0dBUVVGQndFQkJDMHdLekFwQmdnckJnRUZCUWN3QVlZZGFIUjBjSE02THk5MllTMXdjbVZ3Y205a01TNWlZVzVyYVdRdWJtOHdGZ1lEVlIwZ0JBOHdEVEFMQmdsZ2hFSUJFQUVHQVFFd0RnWURWUjBQQVFIL0JBUURBZ1pBTUI4R0ExVWRJd1FZTUJhQUZBRnBreUNjR0RVRk94em01c3l6UEN5cFdvWHVNQjBHQTFVZERnUVdCQlQ0djhOcVZ1OGlzejh0bldhQlZRMlpQb1JXK0RBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQWdFQUlzTHhQa1NiSFdUak9yUWRuN3hNeitFWCtsdGdoNkNRdXJXeXJnZnJIM05pUGE4Z2tnM0xCWFNyTzNZMTloRmdoZmZGc1YzMGJHZ1N5bFIzYjFudUJTMDNTQVBMSEIyMGJDT1I1ejRISDBTRXNtVGlhMGpYMHFBOWFmbklJNS9QV2dJZkpyY1gxR1lCRnBrdDlkQzRZK3ZpT2FKaS9ndHVHV2RHcjBLUWlYOFRBTWJLWjllRnJNSU44VzMxUUwzajIyQ3VOWWJXNU1WQnU4SjNUREszVEQ0dS9VZXFGOU5VZGxFbnFZenFoc0c0R2xEZ1haV0xNUVJPV1lhSEZCQllvT3VwNGM2Qk83UkMydWRzMy9mYzFzUWlzbXBoaWZCWkVNUEdkUTlRVzVFUVBXUWxIL1N3SmpERDlDYSszU0J4YkYvamNUQ2p2eEQwYnlsT016OFJscFBjY1l1S1k2SUtTNkJFWWNkQWJwaVYrWDRTYUxTTkVmclBpV1p0dEhUZ1BHYzFHbDNuUWpBUWcwcDlHWWd2RndsRCtTQWZJS2U3TVlHWk91SEdrTGQ1eHJCSjlxc0JsckV0L0dxSC9kd0ZDNjg1VlRwWDRGWkgveDA1RVdteG8ydjdibklxRGFONWVRTFFGc2F1Y0RxcmhkNWhCSVU4WjFxNGU0S1AvWjkyLzR4SXAzRjM0bWlxeTJUSTg1QzdJamVORmRqcHBLTkgxdGNxQ3VDNTdYVE01Mkh2ME5HR2wwcGg5NmdCeDFpeUV1MExqMms4Zzg3anRTNUIwQ3pTQ1lURUMvajhVcTdtcTN2NSs1cEg2ZFN4Y3JhOUlYUFk0YXUrV1lZMW5uM2QzdlZmR2ZiSWxaMjJ6Zlc5SzQzUXJkd2REMjArUStQMEtxeWw0LytNWnVvd2dnWm1NSUlFVHFBREFnRUNBZ0lGM3pBTkJna3Foa2lHOXcwQkFRc0ZBREJ4TVFzd0NRWURWUVFHRXdKT1R6RXFNQ2dHQTFVRUNnd2hWRVZUVkNBdElFWk9TQ0J2WnlCVGNHRnlaV0poYm10bWIzSmxibWx1WjJWdU1SWXdGQVlEVlFRTERBMVVSVk5VSUMwZ1FtRnVhMGxFTVI0d0hBWURWUVFEREJWVVJWTlVJQzBnUW1GdWEwbEVJRkp2YjNRZ1EwRXdIaGNOTVRneE1USXdNRGt5TmpReVdoY05NekF4TVRJd01Ea3lOalF5V2pCaE1Rc3dDUVlEVlFRR0V3Sk9UekVWTUJNR0ExVUVDZ3dNVkdWemRFSmhibXN4SUVGVE1SSXdFQVlEVlFRTERBa3hNak0wTlRZM09Ea3hKekFsQmdOVkJBTU1Ia0poYm10SlJDQXRJRlJsYzNSQ1lXNXJNU0F0SUVKaGJtc2dRMEVnTXpDQ0FpSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBT0xCSjl6RVRhSkdBSmZoMy9aems1WHBYdWl6WFpFNjBlVVFrTHhLZXB2Um1IaHNtUUpDemtXQ2UrRHRyQ2duS3BXYVFsVXo5WHZCWHdKWmhDcmk4UkcwZytTVE84MXI2c2JiQ3RKbEJkZXRUZml2NjlneStFbHdPUTFEUW5HUW5oNzl5Ylkzd2hiMnEwRzdZbnVHSm84REFqVUcxRVhveGpLSlpxeGN1Y1IxRkh6WmJQSjAvTlJBN2VhdXY3cTc3Y0tmMWdGVjRudTUveU1wS2s0cEhoZGo1UjB0dzZ3cHRXSzFCNWFTVVp3NjNod1V3S1hJdjVqd1FoOTE5cUV3WSt6eWovaEZJNUN1TFpBZ21PVEx5cFVvV3JuNHQwd0N5USsxMVhGWlpBZGlraUd2MTVZdStldXcwUWtqK0gweW1BU3NoK1NOSXY0ZVFqWWFXZ3pMQmRCdkQxRnlZbXM4TFI1OFpQeE1BQ29PbjR6ZlNBbkgzeDF6OGJvdTEzaCs4bzlZTGR5VUtaRVRmSStmdStLMWVZSnZEMnlMTGhYaGVGRFdkOUxwYXF2N010eUVjNGtSR09NdHVxdVUyaTVINFN5T2NsdytUWWQrR3dwMEVyKzFqTzlGeTNJR1ZTcnp0NWlOUEhBN2RWUlNqOStWWHBZckE2bDZnVnZxOExBMFovYWFnSFF6VXorNTZCemp5VEprQmUwNWx1MmxiQ01BNFA0SzgyRVNVcDZjWEpmL3g2WDFIWHFTWnNRT2FWV3RZQ0VyTUdraFdTM1g4SHZkOXZYOGxKMFhrM25XK0NnTUd2amYzNnpUNTNtNVNsVmd5bzBPdkhVWHZZeVRDVHRSMkxSVzNnZGFNZ3lGRWJvc3lOZ3I0ejA5OW16V2VBQmx5MEYrSE5iTUlyRi9BZ01CQUFHamdnRVdNSUlCRWpBU0JnTlZIUk1CQWY4RUNEQUdBUUgvQWdFQU1CVUdBMVVkSUFRT01Bd3dDZ1lJWUlSQ0FSQUJBd0V3VHdZSUt3WUJCUVVIQVFFRVF6QkJNRDhHQ0NzR0FRVUZCekFDaGpOb2RIUndjem92TDJOeWRDMXdjbVZ3Y205a0xtSmhibXRwWkM1dWJ5OTBaWE4wTFdKaGJtdHBaSEp2YjNSallTNWpjblF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQjhHQTFVZEl3UVlNQmFBRk5OR1Z5dDMwbUx2YTh3enh4eGNmak9WS3FJZU1FUUdBMVVkSHdROU1Ec3dPYUEzb0RXR00yaDBkSEJ6T2k4dlkzSnNMWEJ5WlhCeWIyUXVZbUZ1YTJsa0xtNXZMM1JsYzNRdFltRnVhMmxrY205dmRHTmhMbU55YkRBZEJnTlZIUTRFRmdRVUFXbVRJSndZTlFVN0hPYm16TE04TEtsYWhlNHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBR1lFSS9aL242ek4zdWg1cjBpb0lSSkVuejdqZlpWWHI1alByK3lLKzlUMUNlSEw3dU5YcDh0bExQK2dWb0graHVpbDdMOHVxblNHZzRxMFhkVUdlSHJMRlBSWGpIR1NJVHFDNmJnam5qYUxxZ3BIM3ZxV0tMa0ViUTczbVBwOFZCZjVkTWQ1VE1ieEVpVW56cG9mRFl0UUZYYWdUY3lZSWQwaVRsbnF5MXRxL1pHYUZQTmZRdFlSUVVCRlQzNFVYT1BzbXZTZ3U2eENSTjRraFhlNWIrNDNKeXJJNGJ0aWtyS01OUTBOZ2EySGg0UHJ3VndrbEI1aWVZN09LaVRITzJZMFRzNitMVWhraXR3OVBXSk9NM2g5elMxdkJCU2RLNjE0Y21qQ3pMbjNBcDlrVWwzcEJQRUQwd2xSUWluN0lhalhYZm43elZDRUJ5UVdYOHhMVE5VQ2o0d0lmUGpqbnljNU9Cck5xQjVpTzZUbFNQd0dnQVY2LzhpNEV3Q1lNcmRqZWQ4UDQzTUtFZjlWWHlHUWhmQzE4WFBBU1lqb2xIRFBocGxVWWNZQlg2VlhSVjJ5YUxMbHhxQXdnL1hSRlREQ1kxSlEydjZXbkluM0gwakFZT05nRVNhZjRtZGlzaXRxSndVa2s3cDljeTBOQlA2RDU1RXZzcUd6eG1DRjl0Sk50MEI1RzVqdUFkZVI2NWlrb2pOMHNjcjdGYXEvWWUwQkJ5bUZIVUFwSTFlUFFCUGYzT0wwajV5UWQrL1ZVbG9iWGtOUTFxMTArV0dCQWZpQ3Z5VzVaSUREeTJNM09SVG1reHR6L1VuU3RTTkJMZW9RbkNTTUJRTG9IejdycGdKcTRjMFBKc2hrQU5rR0ZQS1U5cmwxb0FuVTlSeDFRVVBrMGJSR3dJL0dNWUlDUXpDQ0FqOENBUUV3YURCaE1Rc3dDUVlEVlFRR0V3Sk9UekVWTUJNR0ExVUVDZ3dNVkdWemRFSmhibXN4SUVGVE1SSXdFQVlEVlFRTERBa3hNak0wTlRZM09Ea3hKekFsQmdOVkJBTU1Ia0poYm10SlJDQXRJRlJsYzNSQ1lXNXJNU0F0SUVKaGJtc2dRMEVnTXdJREllRGdNQXNHQ1dDR1NBRmxBd1FDQWFDQnNUQVlCZ2txaGtpRzl3MEJDUU14Q3dZSktvWklodmNOQVFjQk1Cd0dDU3FHU0liM0RRRUpCVEVQRncweU5UQXlNamN4TVRJd05UbGFNQzhHQ1NxR1NJYjNEUUVKQkRFaUJDQXRETytOWFdmNzNDSzdjOERJVnJ2VmcxbnVhT1I2SHJQbXM0VkUrVVV1V0RCR0Jnc3Foa2lHOXcwQkNSQUNFekUzTURVd016QXhNQzh3Q3dZSllJWklBV1VEQkFJQkJDQ3lZcy90enlQbDZKQWIzbnEvMHd0TjJXeDRSd0FDQW9rYlVEWUlpLzVoVlRBTEJna3Foa2lHOXcwQkFRRUVnZ0VBWFRlVlpXS1JQY0pKd09WcmxaWlpaRHplMWVqaDNzOGhvZU1KZjZaZnFBZU1hSUtiOWUySlZpQ2lNNVRIVXpYVEJhZUEzNFJNRUNFem9uenJZVjlReXpJcGV6UTcrcjBiUTN5bU5zZllNQXFlSCtWRjl6VVA1TXgvNFBhSEZMb1g0TlAvRXQ0bzZHK092OGY3a2xRbjZEclEzaGlzMXUrM3ZNQnJGMGkyZHVpSXhzeFJaVGVUalZpWUFXV0IvUHJxcTEvTmMyQXczT2hmeHB1OW5TOUV4Ukt0UDN6blUvdFpiLzdpSWdOWklWeExET0FicUxkeHl3ei90Zm81eVpnYnljbU04SnhrOFVqZlVjSmtjRGRYaVBueVpQdHRQb1VFRVliNDJxNHo5VlAvWXBuRmp0UCtnQWxwQlQyYWxiZUd3RGpMVzZQRXBaSkxLYWE5UkZqcG5nPT08L0NNU1NpZ25hdHVyZT48VXNlckNlcnRpZmljYXRlQW5kUmV2b2NhdGlvbkRhdGE+PFJldm9jYXRpb25WYWx1ZXM+PFhBZEVTOk9DU1BWYWx1ZXM+PFhBZEVTOkVuY2Fwc3VsYXRlZE9DU1BWYWx1ZT5NSUlHd3pDQjVxRlJNRTh4Q3pBSkJnTlZCQVlUQWs1UE1SY3dGUVlEVlFRS0RBNVVSVk5VSUVKaGJtdEpSQ0JCVXpFTk1Bc0dBMVVFQ3d3RVZFVlRWREVZTUJZR0ExVUVBd3dQVkdWemRDQkNZVzVyU1VRZ1ZrRXpHQTh5TURJMU1ESXlOekV4TWpFd01Gb3dVVEJQTURvd0J3WUZLdzREQWhvRUZETEJkUEJzM1JocUV3TDlweTlaMjBhMlpXTHRCQlFCYVpNZ25CZzFCVHNjNXViTXN6d3NxVnFGN2dJREllRGdnQUFZRHpJd01qVXdNakkzTVRFeU1UQXdXcUV0TUNzd0tRWUpLd1lCQlFVSE1BRUNCQnhhYTB0alpVdFZZbk5uUzFwaWVIaGFNMFp4TnpGQmJIaGFOMVU5TUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBblBGVEo3SjJWcFdNQm5HcTVJV1UxYTRUS3BWdXhMUUNzWVpJcFd0Q0FwWEFSZTdMYUR4K1hCWVVQTHFvZ3hLNGcxMUFKUFFoWEh3dW5uTzlpVHI4NXJZdGc2ZkhaTUpGZEhZVW03bkorNENZb01xcUdzcTlqNjR0d0hRZHczcGVQc1hEbk53M29QQUhuVjEzbGlhS3pzdythRWZ3ZGpoQWN6Y2lUTUQ2WGo2YU9JT2ZReVlGU0R1Y1VJRHdZeHlqZW9ZSDJGUWtJdHltQWtvKzRhVWlmR0JWQWdGME9QUEE3b0NmMjhCSmFHSXB1cnFkYml3algvUEN2Qm1meHorL3JGSTk0bXlCbmNDWXFWYlFnQkRSbzlVNjFlZXgxb213TnJSNzZJdWJDbGVIaDU1RGIrbWI3ajdqN3M5L0tBY0FOMkhwVWxmQmUvQ0JhazdpcGdsMFlvSUlFd2pDQ0JMNHdnZ1M2TUlJQ29xQURBZ0VDQWdJSkNUQU5CZ2txaGtpRzl3MEJBUXNGQURCeE1Rc3dDUVlEVlFRR0V3Sk9UekVxTUNnR0ExVUVDZ3doVkVWVFZDQXRJRVpPU0NCdlp5QlRjR0Z5WldKaGJtdG1iM0psYm1sdVoyVnVNUll3RkFZRFZRUUxEQTFVUlZOVUlDMGdRbUZ1YTBsRU1SNHdIQVlEVlFRRERCVlVSVk5VSUMwZ1FtRnVhMGxFSUZKdmIzUWdRMEV3SGhjTk1qTXdNVEV3TVRReU5EVTVXaGNOTWpjd01URXdNVFF5TkRVNVdqQlBNUXN3Q1FZRFZRUUdFd0pPVHpFWE1CVUdBMVVFQ2d3T1ZFVlRWQ0JDWVc1clNVUWdRVk14RFRBTEJnTlZCQXNNQkZSRlUxUXhHREFXQmdOVkJBTU1EMVJsYzNRZ1FtRnVhMGxFSUZaQk16Q0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU9OSEZlL3RRcUN2WGY4enAwYlM5U2NWSXoreGJmU2xYR2FKTEUyVGRITzc5VDUybk9KV3Y2WTQzdkNwdmtobktoRzc2Y0QvNnJiMmtrVXNVRXFqUTA3SlNCSFYzK2pLYWZSNmFZSnNoZmZDc1hkSnhhYWVUU1lxeU1COEdDQTlQKzBSNDA4WkZCUjVBSzhqRHhaeW91ZElyK0dqemt2bG1pZ0l6em5ERFVTVlZGUU9mQmdCMHRCbnVEemR5eEZsN1VXL3B2NFUrTXJONHhhMTZjcTBlWjA5MWhIaWRwQnFmOE5ZWDJ2K1QyS3N6Uzc3QWFrczZNb0dmM3I5M3RSYktXRUkwSlFiczhESTVmTDRabnBqN1IxT3lQOHg4dnpGOGZ2RWZDRTJHdjgxOEtOeUpSS1E5VzJSZEM2VitCNndpUDIyL0xiYlBBQ21oRWhyaDVid3hqTUNBd0VBQWFOK01Id3dGUVlEVlIwZ0JBNHdEREFLQmdoZ2hFSUJFQUVGQVRBT0JnTlZIUThCQWY4RUJBTUNCc0F3RXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVIQXdrd0h3WURWUjBqQkJnd0ZvQVUwMFpYSzNmU1l1OXJ6RFBISEZ4K001VXFvaDR3SFFZRFZSME9CQllFRkpKUHQ4eHdwZUJQOUZzRXVIa05GWm1VSzJwdk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQ0FRQlA1Nk1VL1E1c1BDZHA4S3MvalZkbWxseURPWFhWQzNZUEV0b3RBRk1kRWlHb0ZQcG4zMmhqY0lJejVBUE1oZzZUWHdFejdQZHlmY21KazBQV01sbEN2V3Z4RGp5c1lBWVNENTBraXZuK2lGZ0tzN0NyMTAwTzVBbjAybzJpd3d2d1BWck9nR1Z2aVJEUk1YalB5WVN3NDkxczhKM25yaEI5dVhkTXlCbHJvQTNPdEdTeVlOL09KUklBNVRxL2JQdzVIVWN0ZDJTQWF0OVQ0VHJ3d2ZFWXFsTExwckZkSUQ5VEVuMVZpdUFlZmtwZ3lPbWVNMituWDdJUHFwbmVQU0IrL203Z0wwaXhrck10UENoY2NKdC9TS0xJcTlRMmhkMFRiOUNOL2pWNy9hQXZDSlZ1OWxPeHBxN0JJU0FVOXM5Ny90T1I1WUd2Q05ZbmFia0lYOGpWRFZrVmY4VjZsb2t1RG5JUWJjWEVzeXpFcmNmV2dFSkFTdnFHcWw2dElabXA3dFluSlozY0NPQm1IQmhIaVZtOFU5eVVYTnRxelhyTTJBY085aE54VllJM0VRRHR3U3E4eld4b3lwcnI0MTZ5QlA4UTV3NGVsakRtcGhnMmN3QXpxWXl0Vm5lakpKM0ZuQmx5Vk9MUlFiOWw2NVl3dnpPQ2t2Y0FqQkUzalZNL0k4RXh5WGgwSWFVM1llZGFhaTljcVZVdzY0RzROYTlMMVNoaXFZU0VRMWx4RStGZXJVMTlXd0RKMHVOSXNyY2lXQTJzcy9PQ0FjU01ndHA4TzJTS3oyOFhsZUViZy9VS2hoOXlyNktwQlhobDhLbmdaY0VHY0ZyR2RJOWpVdjJTSFhRQjRvcWtDVEo1dVBMeTlFa2FHQWRWR1dRZjZnOWliN1l6L29KQStBPT08L1hBZEVTOkVuY2Fwc3VsYXRlZE9DU1BWYWx1ZT48L1hBZEVTOk9DU1BWYWx1ZXM+PC9SZXZvY2F0aW9uVmFsdWVzPjwvVXNlckNlcnRpZmljYXRlQW5kUmV2b2NhdGlvbkRhdGE+PC9DTVNTaWduYXR1cmVFbGVtZW50PjwvU0RPU2lnbmF0dXJlPjwvU0RPU2VhbD48TWV0YWRhdGE+PFZhbHVlUGFpcj48TmFtZT5NZXJjaGFudERlc2M8L05hbWU+PFZhbHVlPmhhc2hUb1NpZ248L1ZhbHVlPjwvVmFsdWVQYWlyPjwvTWV0YWRhdGE+PC9TRE8+PC9TRE9MaXN0Pg==";

    public static void main(String[] args) {
        System.out.println("sdo document with added: ");
        System.out.println(
                addDocumentDataToSDO(
                        bytesSigned,
                        sdoWithoutDocumentB64));
    }

    static String addDocumentDataToSDO(byte[] documentBytesSigned, String sdoWithoutDocumentB64) {
        try {
            String xml = new String(
                    Base64.getDecoder().decode(sdoWithoutDocumentB64), StandardCharsets.UTF_8);

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));

            // Use XPath to find the desired node
            XPathFactory xPathFactory = XPathFactory.newInstance();
            XPath xpath = xPathFactory.newXPath();
            XPathExpression expr = xpath.compile("/SDOList/SDO");
            Node sdoNode = (Node) expr.evaluate(document, XPathConstants.NODE);

            if (sdoNode != null) {
                // Add the SignedObject node with SignersDocument content to the SDO node
                Element signersDocument = document.createElement("SignersDocument");

                // The document data should be added as the Base64 encoded bytes of the signed bytes
                signersDocument.setTextContent(Base64.getEncoder().encodeToString(documentBytesSigned));

                Element signedObject = document.createElement("SignedObject");
                signedObject.appendChild(signersDocument);
                sdoNode.appendChild(signedObject);
                TransformerFactory transformerFactory = TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource source = new DOMSource(document);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                StreamResult result = new StreamResult(outputStream);
                transformer.transform(source, result);

                // The output will be the updated XML document with the SignedObject node added. Should pass the NETS SDO validation.

                return outputStream.toString();

            } else {
                throw new IllegalArgumentException("SDO node not found in the XML document");
            }
        } catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException |
                 TransformerException e) {
            throw new RuntimeException("Some XML error", e);
        }

    }
}

Computing SHA256 hashes

When the client is sending hashes of documents instead of the documents themselves, the client must compute the hash in the same way as BankID would have done. BankID extracts the bytes for computing the sha256 hash for a text, PDF or bidxml based from the documents the following ways below:

  • For text, the bytes are extracted from the text string using the ISO_8859_1 charset
  • For PDF, the bytes are the bytes read from the PDF file as is.
  • For bidxml, the XML and XSL string are wrapped into a string as below, and then the bytes are extracted using the ISO_8859_1 charset.
    import java.util.Base64;
    /**
     * Compute the sha256 hash of the given byte array and return it as a base64 encoded string
     */
    private static String sha256B64(byte[] tbs) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(tbs);
            return Base64.getEncoder().encodeToString(hash);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Compute the sha256 hash by converting the text to bytes using the ISO_8859_1 charset
     */
    public static String sha256ForTextB64(String textDocument) {
        return sha256B64(textDocument.getBytes(StandardCharsets.ISO_8859_1));
    }

    /**
     * Compute the sha256 hash by using the pdf bytes as is
     */
    public static String sha256ForPdf(byte[] pdfDoc) {
        return sha256B64(pdfDoc);
    }

    /**
     * Compute the sha256 hash by wrapping the xml and xsl parts into a BankIDXML structure and then converting the string to bytes using the ISO_8859_1 charset
     */
    public static String sha256ForBidXml(String xmlDocument, String xslDocument) {
        return sha256forTextB64(
                "<BankIDXML><BIDXML>" + xmlDocument + "</BIDXML><BIDXSL>" + xslDocument + "</BIDXSL></BankIDXML>");
    }