Signature protocols
The primary use case of RP API is to provide digital signatures. In RP API v3, new signature protocols for authentication and signing use cases have been introduced in order to maximize the security benefits for each use case:
-
Signature protocol
ACSP_V2
- for authentication requests, -
Signature protocol
RAW_DIGEST_SIGNATURE
- for signing requests.
ACSP_V2
signature protocol
Authentication Context Signature Protocol (ACSP) is a protocol that is used for authentication requests. In ACSP_v2
, the final data-to-be-signed object incorporates multiple factors such as a challenge from the Relying Party (rpChallenge), a server-provided random factor (serverRandom), and a random factor from the user’s mobile device (userChallenge).
ACSP_V2
signature request parameters
For ACSP_V2
signatureProtocol
and signatureProtocolParameters
parameters in the authentication requests and examples, select an authentication endpoint in OpenAPI specification.
rpChallenge
generation
Generation of rpChallenge
can be done as follows:
rpChallenge := BASE64-ENCODE(CRYPTO-RANDOM(64))
-
Java
-
PHP
-
Python
-
Go
-
Rust
-
Kotlin
-
C#
-
Node.js
-
Ruby
import java.security.SecureRandom;
import java.util.Base64;
public class RandomGenerateBase64Encode {
public static void main(String[] args) {
byte[] rpChallengeBytes = new byte[64];
new SecureRandom().nextBytes(rpChallengeBytes);
String rpChallenge = Base64.getEncoder()
.encodeToString(rpChallengeBytes);
System.out.println(rpChallenge);
}
}
<?php
$rpChallengeBytes = random_bytes(64);
$rpChallenge = base64_encode($rpChallengeBytes);
echo $rpChallenge;
?>
import os
import base64
rp_challenge_bytes = os.urandom(64)
rp_challenge = base64.b64encode(rp_challenge_bytes)
print(rp_challenge)
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
)
func main() {
var rpChallengeBytes []byte = make([]byte, 64)
_, err := rand.Read(rpChallengeBytes)
if err != nil {
fmt.Printf("error: %s\n", err)
return
}
var rpChallenge []byte = []byte(base64.StdEncoding.EncodeToString([]byte(rpChallengeBytes)))
fmt.Printf("%q\n", rpChallenge)
}
use base64::Engine as _;
use rand::RngCore;
use rand::rngs::OsRng;
fn main() {
let mut rp_challenge_bytes: [u8; 64] = [0u8; 64];
OsRng.fill_bytes(&mut rp_challenge_bytes);
let rp_challenge_string: String = base64::engine::general_purpose::STANDARD.encode(rp_challenge_bytes);
let rp_challenge: &[u8] = rp_challenge_string.as_bytes();
println!("{:?}\n{}", rp_challenge, rp_challenge_string);
}
import java.security.SecureRandom
import java.util.Base64
fun main() {
val random = SecureRandom()
val rpChallengeBytes: ByteArray = ByteArray(64)
random.nextBytes(rpChallengeBytes)
val rpChallenge: String = Base64.getEncoder()
.encodeToString(rpChallengeBytes)
println(rpChallenge)
}
using System;
public class RandomGenerateBase64Encode
{
public static void Main(string[] args)
{
byte[] rpChallengeBytes = new byte[64];
new Random().NextBytes(rpChallengeBytes);
string rpChallenge = Convert.ToBase64String(rpChallengeBytes);
Console.WriteLine(rpChallenge);
}
}
const crypto = require('crypto');
const rpChallengeBytes = crypto.randomBytes(64);
const rpChallenge = rpChallengeBytes.toString('base64');
console.log(rp_challenge);
require 'base64'
require 'securerandom'
rp_challenge_bytes = SecureRandom.random_bytes(64)
rp_challenge = Base64.strict_encode64(rp_challenge_bytes)
puts rp_challenge
ACSP_V2
signature response parameters
For ACSP_V2
signature
parameter in the authentication response and examples, see GET /v3/session/{sessionID} in OpenAPI specification. This is an example ACSP_V2
signature object that is returned in the response:
"signature": {
"value": "Vak2Q0NiFnh6+lW+YaJuB8yMYM7k3I5QfsUxS3Y1Ddm3qy6HvebLl0/t17dq289/+4mGx45qnHVNj1CzqF88lYFpwAQXFc5XiHtYbvnENgjwLSfrQ9mrSt0phemAK29GzAexilPPy5+PdeCdO+TEuMIhi/qkKhL+lk5d1flmrWZQ4B0H7kXTqRjJvCAUc8bsdM7SLV5jZuEIhOqTCsyDY3FZmyJi4ms8SsOmYCy/Do20CIiQbv75eunKfTKciqVnOA+WYN5OXLJVqhgaHJ+hYiYA+QXOvIIYKVIUdB1rDRnKwwvZk1lZ3oKW1r7XGkovbmwRz+NpmHTyMUubXXQHYah3T4h7vf0C14MBpupWRB47s6rsdPZzH2No7AoDGjMGAMEWg5rdB45fYXbjRV2T69e4c47VpubG1DXTsoXowe/yzXdIjUYfZDwozzW6+IXTdAKL4LP6wmCU+PxP/GfYQ1k1w3fXjh4XeO9QmipRDEle2l0z6nYJaGuIYCb7pDh6ZoJllyKSmhqWG0PMLod34Lo1MMP4WGvLe86/fiTFPbh/VR981MIjz5xIRaSxFZQmNzI5IvzORskgsHGALb7Nb51q3jPKE2D1UOrQN6N6DMbGFMFR/7vRgkvUbeC0jAWevVrssEK77d5OmgXqk7sevYDwP1WnmBwr2ov/Tt+AM58BTgdHsnWRw6hL1mS9+DzCaCuDb/+vspiZnVjP9S9ckmCLP8yrwzL8Myj5lS62Rta727GulSUet21euQl0E2CuDpSoiKuO3NkYWqskfCowb0GiiX0oZe98mFiBFo1Za5Tb207fPOQyGAwY29k0/XaYxwMPsU7/hwI5Ba8iek1A09Et3HB0q0hXeKyWl425ZrMdVoOMnVyquBNo/FhAOvoSGUyiIUSDdpSwgpiJAH5cVX3W6lOy93mNtPBMk0qxtItYD3b9N7Lut6SCLhL/IGoNmrouOUX3xM8KD8qgnXmUXF0996YK9WxFfZ8HScFVno8kRZdDY4QinqcwZy+FNXBG",
"serverRandom": "+wVP2U/SMKVkVrggDjNTXFV/",
"userChallenge": "TLSjYRH2oYw8tW2bq0it0IUb7WIFkCLgF8NTc7-4Zq4",
"flowType": "Notification",
"signatureAlgorithm": "rsassa-pss",
"signatureAlgorithmParameters": {
"hashAlgorithm": "SHA-512",
"maskGenAlgorithm": {
"algorithm": "id-mgf1",
"parameters": {
"hashAlgorithm": "SHA-512"
}
},
"saltLength": 64,
"trailerField": "0xbc"
}
}
ACSP_V2
digest calculation
To verify the signature.value
the RP must first reconstruct its input digest (hash). The digest is calculated over the ACSP_V2_payload
, and ACSP_V2_payload
is assembled using vertical bar character |
(U+007C) as a separator.
Rules of generating the digest
value:
-
in case
initialCallbackUrl
does not exist (for QR and Notification flows), this field must be empty and the separator must still be used, i.e.||
; -
in case the RP is not acting as a broker, the
brokeredRpName
must be empty and the separator must still be used, i.e.||
; -
interactions
must be the same Base64-encoded byte string value that was used to initiate the session. Since JSON to Base64 encoding is non-deterministic (parameters may be ordered randomly, whitespace can be optional), trying to encode the same object again might return a different value; -
the digest must be calculated from
UTF-8
encoded byte array that consists of the described elements joined in the specified order with vertical bar character|
(U+007C) as a separator, this also applies to Base64 encoded values, they must be used as is without Base64 decoding; -
Base64 encoding must also make sure that the unused bits are set to
0
and that the resulting values are correctly padded with=
signs; -
the hash algorithm to use is specified in
signature.signatureAlgorithmParameters.hashAlgorithm
. The signature scheme that was used to generate the signature based on the digest is specified insignature.signatureAlgorithm
.
message := STR-CONCAT(
'smart-id', '|',
'ACSP_V2', '|',
serverRandom, '|',
rpChallenge, '|',
userChallenge, '|',
BASE64-ENCODE(relyingPartyName), '|',
BASE64-ENCODE(brokeredRpName), '|',
BASE64-ENCODE(SHA-256(interactions)), '|',
interactionTypeUsed, '|',
initialCallbackUrl, '|',
flowType)
digest := HASH(UTF8-ENCODE(message))
-
Java
-
PHP
-
Python
-
Go
-
Rust
-
Kotlin
-
C#
-
Node.js
-
Ruby
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
public class AcspV2DigestCalculator {
public static void main(String[] args) throws Exception {
String separator = "|";
String schemeName = "smart-id";
String signatureProtocol = "ACSP_V2";
String serverRandom = "MTlop6EXCrQ6FOErcKjxUhbV";
String rpChallenge = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==";
String userChallenge = "GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00";
String relyingPartyNameBase64 = "REVNTw==";
String brokeredRpNameBase64 = "RXhhbXBsZSBSUA==";
String interactions = "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d";
String interactionTypeUsed = "confirmationMessage";
String initialCallbackUrl = "https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ";
String flowType = "Web2App";
MessageDigest md256 = MessageDigest.getInstance("SHA-256");
md256.update(interactions.getBytes(StandardCharsets.UTF_8));
byte[] interactionsHash = md256.digest();
String interactionsBase64 = Base64.getEncoder().encodeToString(interactionsHash);
String[] payloadParts = {
schemeName,
signatureProtocol,
serverRandom,
rpChallenge,
userChallenge,
relyingPartyNameBase64,
brokeredRpNameBase64,
interactionsBase64,
interactionTypeUsed,
initialCallbackUrl,
flowType
};
byte[] acspV2Payload = String
.join(separator, payloadParts)
.getBytes(StandardCharsets.UTF_8);
MessageDigest md512 = MessageDigest.getInstance("SHA-512");
md512.update(acspV2Payload);
byte[] digest = md512.digest();
System.out.println(Base64.getEncoder().encodeToString(digest));
}
}
<?php
$separator = '|';
$hashAlgorithm = 'sha512';
$schemeName = 'smart-id';
$signatureProtocol = 'ACSP_V2';
$serverRandom = 'MTlop6EXCrQ6FOErcKjxUhbV';
$rpChallenge = 'GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==';
$userChallenge = 'GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00';
$relyingPartyNameBase64 = 'REVNTw==';
$brokeredRpNameBase64 = 'RXhhbXBsZSBSUA==';
$interactions = 'W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d';
$interactionTypeUsed = 'confirmationMessage';
$initialCallbackUrl = 'https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ';
$flowType = 'Web2App';
$interactionsHash = hash('sha256', $interactions, true);
$interactionsBase64 = base64_encode($interactionsHash);
$acspV2Payload = implode($separator, [
$schemeName,
$signatureProtocol,
$serverRandom,
$rpChallenge,
$userChallenge,
$relyingPartyNameBase64,
$brokeredRpNameBase64,
$interactionsBase64,
$interactionTypeUsed,
$initialCallbackUrl,
$flowType
]);
$digest = hash($hashAlgorithm, $acspV2Payload, true);
echo base64_encode($digest);
?>
import base64
import hashlib
separator = '|'
scheme_name = 'smart-id'
signature_protocol = 'ACSP_V2'
server_random = 'MTlop6EXCrQ6FOErcKjxUhbV'
rp_challenge = 'GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=='
user_challenge = 'GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00'
relying_party_name_base64 = 'REVNTw=='
brokered_rp_name_base64 = 'RXhhbXBsZSBSUA=='
interactions = 'W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d'
interaction_type_used = 'confirmationMessage'
initial_callback_url = 'https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ'
flow_type = 'Web2App'
interactions_hash = hashlib.sha256(interactions.encode('utf-8')).digest()
interactions_base64 = base64.b64encode(interactions_hash).decode('utf-8')
acsp_v2_payload = separator.join([
scheme_name,
signature_protocol,
server_random,
rp_challenge,
user_challenge,
relying_party_name_base64,
brokered_rp_name_base64,
interactions_base64,
interaction_type_used,
initial_callback_url,
flow_type
]).encode('utf-8')
digest = hashlib.sha512(acsp_v2_payload).digest()
print(base64.b64encode(digest))
package main
import (
"bytes"
"crypto/sha512"
"encoding/base64"
"fmt"
)
func main() {
var separator string = "|"
var schemeName string = "smart-id"
var signatureProtocol string = "ACSP_V2"
var serverRandom string = "MTlop6EXCrQ6FOErcKjxUhbV"
var rpChallenge string = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=="
var userChallenge string = "GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00"
var relyingPartyNameBase64 string = "REVNTw=="
var brokeredRpNameBase64 string = "RXhhbXBsZSBSUA=="
var interactions string = "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d"
var interactionTypeUsed string = "confirmationMessage"
var initialCallbackUrl string = "https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ"
var flowType string = "Web2App"
var interactionsHash [32]byte = sha256.Sum256([]byte(interactions))
var interactionsBase64 string = base64.StdEncoding.EncodeToString(interactionsHash[:])
var acspv2Payload string = strings.Join([]string{
schemeName,
signatureProtocol,
serverRandom,
rpChallenge,
userChallenge,
relyingPartyNameBase64,
brokeredRpNameBase64,
interactionsBase64,
interactionTypeUsed,
initialCallbackUrl,
flowType,
}, separator)
var digest [64]byte = sha512.Sum512([]byte(acspv2Payload))
fmt.Printf("%q\n", base64.StdEncoding.EncodeToString(digest[:]))
}
use base64::Engine as _;
use sha2::{Sha256, Sha512, Digest};
fn main() {
let separator: &str = "|";
let scheme_name: &str = "smart-id";
let signature_protocol: &str = "ACSP_V2";
let server_random: &str = "MTlop6EXCrQ6FOErcKjxUhbV";
let rp_challenge: &str = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==";
let user_challenge: &str = "GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00";
let relying_party_name_base64: &str = "REVNTw==";
let brokered_rp_name_base64: &str = "RXhhbXBsZSBSUA==";
let interactions: &str = "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d";
let interaction_type_used: &str = "confirmationMessage";
let initial_callback_url: &str = "https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ";
let flow_type: &str = "Web2App";
let interactions_hash = Sha256::digest(interactions.as_bytes());
let interactions_base64 = &base64::engine::general_purpose::STANDARD.encode(interactions_hash);
let acsp_v2_payload_parts: [&str; 10] = [
scheme_name,
signature_protocol,
server_random,
rp_challenge,
user_challenge,
relying_party_name_base64,
brokered_rp_name_base64,
interactions_base64,
interaction_type_used,
initial_callback_url,
flow_type
];
let acsp_v2_payload: String = acsp_v2_payload_parts.join(separator);
let mut hasher = Sha512::new();
hasher.update(acsp_v2_payload.as_bytes());
let digest: Vec<u8> = hasher.finalize().to_vec();
println!("{:?}", base64::engine::general_purpose::STANDARD.encode(digest))
}
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.Base64
fun main() {
val separator: String = "|"
val schemeName: String = "smart-id"
val signatureProtocol: String = "ACSP_V2"
val serverRandom: String = "MTlop6EXCrQ6FOErcKjxUhbV"
val rpChallenge: String = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=="
val userChallenge: String = "GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00"
val relyingPartyNameBase64: String = "REVNTw=="
val brokeredRpNameBase64: String = "RXhhbXBsZSBSUA=="
val interactions: String = "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d"
val interactionTypeUsed: String = "confirmationMessage"
val initialCallbackUrl: String = "https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ"
val flowType: String = "Web2App"
val interactionsHash: ByteArray = MessageDigest
.getInstance("SHA-256")
.digest(interactions.toByteArray(StandardCharsets.UTF_8))
val interactionsBase64: String = Base64
.getEncoder()
.encodeToString(interactionsHash)
val acspV2Payload: ByteArray = listOf(
schemeName,
signatureProtocol,
serverRandom,
rpChallenge,
userChallenge,
relyingPartyNameBase64,
brokeredRpNameBase64,
interactionsBase64,
interactionTypeUsed,
initialCallbackUrl,
flowType
)
.joinToString(separator)
.toByteArray(StandardCharsets.UTF_8)
val acspV2Digest: ByteArray = MessageDigest
.getInstance("SHA-512")
.digest(acspV2Payload)
println(Base64.getEncoder().encodeToString(acspV2Digest))
}
using System;
using System.Security.Cryptography;
using System.Text;
public class AcspV2DigestCalculator
{
public static void Main(string[] args)
{
string separator = "|";
string schemeName = "smart-id";
string signatureProtocol = "ACSP_V2";
string serverRandom = "MTlop6EXCrQ6FOErcKjxUhbV";
string rpChallenge = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==";
string userChallenge = "GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00";
string relyingPartyNameBase64 = "REVNTw==";
string brokeredRpNameBase64 = "RXhhbXBsZSBSUA==";
string interactions = "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d";
string interactionTypeUsed = "confirmationMessage";
string initialCallbackUrl = "https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ";
string flowType = "Web2App";
byte[] interactionsHash = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(interactions));
string interactionsBase64 = Convert.ToBase64String(interactionsHash);
string[] payloadParts = {
schemeName,
signatureProtocol,
serverRandom,
rpChallenge,
userChallenge,
relyingPartyNameBase64,
brokeredRpNameBase64,
interactionsBase64,
interactionTypeUsed,
initialCallbackUrl,
flowType
};
byte[] acspV2Payload = Encoding.UTF8.GetBytes(
string.Join(separator, payloadParts)
);
byte[] digest = SHA512.Create().ComputeHash(acspV2Payload);
string base64Digest = Convert.ToBase64String(digest);
Console.WriteLine(base64Digest);
}
}
const crypto = require('crypto');
const separator = '|';
const schemeName = 'smart-id';
const signatureProtocol = 'ACSP_V2';
const serverRandom = 'MTlop6EXCrQ6FOErcKjxUhbV';
const rpChallenge = 'GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==';
const userChallenge = 'GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00';
const relyingPartyNameBase64 = 'REVNTw==';
const brokeredRpNameBase64 = 'RXhhbXBsZSBSUA==';
const interactions = 'W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d';
const interactionTypeUsed = 'confirmationMessage';
const initialCallbackUrl = 'https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ';
const flowType = 'Web2App';
const interactionsBase64 = crypto.createHash('sha256').update(interactions).digest('base64');
const acspV2Payload = [
schemeName,
signatureProtocol,
serverRandom,
rpChallenge,
userChallenge,
relyingPartyNameBase64,
brokeredRpNameBase64,
interactionsBase64,
interactionTypeUsed,
initialCallbackUrl,
flowType
].join(separator);
const digest = crypto.createHash('sha512').update(acspV2Payload).digest('base64');
console.log(digest);
require 'base64'
require 'digest'
separator = '|'
scheme_name = 'smart-id'
signature_protocol = 'ACSP_V2'
server_random = 'MTlop6EXCrQ6FOErcKjxUhbV'
rp_challenge = 'GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=='
user_challenge = 'GnsWXXEjTCKR89fj9uo5u5ReBZ9JR7_pezLAI5jMS00'
relying_party_name_base64 = 'REVNTw=='
brokered_rp_name_base64 = 'RXhhbXBsZSBSUA=='
interactions = 'W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d'
interaction_type_used = 'confirmationMessage'
initial_callback_url = 'https://rp.example.com/callback-url?value=RrKjjT4aggzu27YBddX1bQ'
flow_type = 'Web2App'
interactions_hash = Digest::SHA256.digest(interactions)
interactions_base64 = Base64.strict_encode64(interactions_hash)
acsp_v2_payload = [
scheme_name,
signature_protocol,
server_random,
rp_challenge,
user_challenge,
relying_party_name_base64,
brokered_rp_name_base64,
interactions_base64,
interaction_type_used,
initial_callback_url,
flow_type
].join(separator)
digest = Digest::SHA512.digest(acsp_v2_payload)
puts Base64.strict_encode64(digest)
ACSP_V2
signature digest (in Base64 format)pKOjbNl/5Fy8NfrFqsj6pSn8W8O+Ik8rM33QSsbyD3J9qDJvEm90SboUciuY4wHGWa0Pnq8BgT3NJKmJiUDfKg==
Full verification of the signature and response is described in Response verification.
RAW_DIGEST_SIGNATURE
signature protocol
RAW_DIGEST_SIGNATURE
is a signature protocol where a pre-calculated digest (hash) of the data-to-be-signed is sent in the initial request, and the digital signature is generated directly on this provided digest. For verification, the digest itself or the complete original data that corresponds to the digest must be used.
RAW_DIGEST_SIGNATURE
signature request parameters
For RAW_DIGEST_SIGNATURE
signatureProtocol
and signatureProtocolParameters
parameters in the signature requests and examples, see the API spec signature request parameters.
RAW_DIGEST_SIGNATURE
signature response parameters
For RAW_DIGEST_SIGNATURE
signature
parameter in the signature response and examples, see the API spec session status response parameters.
Full verification of the signature and response is described in Response verification.