Skip to content

Build your rQES Core for Android

The EUDI rQES Core SDK provides the foundational service logic for enabling Remote Qualified Electronic Signatures (RQES) in Android applications. This document explains how to integrate and use the Core SDK in your project.

Overview

This SDK provides the core functionality for an EUDI Wallet to interact with a remote Qualified Electronic Signature (rQES) service. It handles service authorisation, credential authorisation, and document signing.

Requirements

Android 10 (API level 29) or higher

Installation

To include the library in your project, add the following dependencies to your app's build.gradle file.

dependencies {
    // EUDI Wallet rQES service library
    implementation("eu.europa.ec.eudi:eudi-lib-android-rqes-core:0.4.0")
}

Integration guide

The following diagram illustrates the high-level steps of the rQES document signing flow, from service authorisation to obtaining the final signed documents.

sequenceDiagram
    participant Client
    participant rQESService
    participant rQESService.Authorized
    participant rQESService.CredentialAuthorized
    Client ->>+ rQESService: getRSSPMetadata()
    rQESService -->>- Client: RSSPMetadata
    Client ->>+ rQESService: getServiceAuthorizationUrl()
    rQESService -->>- Client: HttpsUrl
    Client ->>+ rQESService: authorizeService(authorizationCode)
    rQESService -->>- Client: RQESService.Authorized
    Client ->>+ rQESService.Authorized: listCredentials(request)
    rQESService.Authorized -->>- Client: List<CredentialInfo>
    Client ->>+ rQESService.Authorized: getCredentialAuthorizationUrl(credential, UnsignedDocuments)
    rQESService.Authorized -->>- Client: HttpsUrl
    Client ->>+ rQESService.Authorized: authorizeCredential(authorizationCode)
    rQESService.Authorized -->>- Client: RQESService.CredentialAuthorized
    Client ->>+ rQESService.CredentialAuthorized: signDocuments()
    rQESService.CredentialAuthorized -->>- Client: SignedDocuments

1. Create an rQESService instance

val rqesService = rQESService(
    serviceEndpointUrl = "https://example.com/csc/v2",
    config = CSCClientConfig(
        client = OAuth2Client.Confidential.ClientSecretBasic(
            clientId = "client-id",
            clientSecret = "client-secret"
        ),
        authFlowRedirectionURI = URI("rqes:redirect"),
    ),
    outputPathDir = "/path/to/output/dir",
    // set the hashing algorithm that will be used
    // default is SHA-256 as shown below
    hashAlgorithm = HashAlgorithmOID.SHA_256,
    // optionally provide a HttpClientFactory to create a HttpClient for the service
    // this is useful for logging, testing, etc.
    httpClientFactory = {
        // create a HttpClient
        HttpClient(/* Configure */)
    }
)

You can fetch the rQES service metadata:

val metadata = rqesService.getRSSPMetadata().getOrThrow()

2. Authorise the rQES service

To begin the signing process, the user must authorise your application to use the rQES service.

First, obtain the service authorisation URL and open it in a custom tab or browser:

val authorizationUrl = rqesService.getServiceAuthorizationUrl().getOrThrow()

// Open the authorizationUrl in a browser
// After the user has authorised the service, the browser will be redirected to the authFlowRedirectionURI that
// is configured in the `CSCClientConfig`
// with a query parameter named "code" containing the authorisation code

After the user grants authorisation, the service will redirect to the authFlowRedirectionURI you configured, appending an authorisation code as a query parameter. Your app must capture this redirect, extract the code, and exchange it for an access token.

val authorizationCode = AuthorizationCode("code")
val authorizedService = rqesService.authorizeService(authorizationCode).getOrThrow()

3. Select a credential and prepare documents

Once the service is authorised, you can list the user's available signing credentials.

val credentials = authorizedService.listCredentials().getOrThrow()
val credential = credentials.first() // choose whichever credential you want

Next, prepare the documents that need to be signed.

val unsignedDocuments = UnsignedDocuments(
    UnsignedDocument(
        label = "Document to sign",
        file = File("document.pdf"),
        // Optionally override default signing configuration
        signingConfig = UnsignedDocument.Config(
            signatureFormat = SignatureFormat.P,
            conformanceLevel = ConformanceLevel.ADES_B_B,
            signedEnvelopeProperty = SignedEnvelopeProperty.ENVELOPED,
            asicContainer = ASICContainer.NONE
        )
    )
)

4. Authorise the credential and sign

The user must now authorise the use of their selected credential for this specific transaction. 

Obtain the credential authorizationURL to open a browser and let the user authorise the credential.

val credentialAuthorizationUrl = authorizedService.getCredentialAuthorizationUrl(
    credential = credential,
    documents = unsignedDocuments,
    // Optional: signing algorithm. If omitted, the first supported algorithm of the credential is used.
    signingAlgorithmOID = SigningAlgorithmOID.ECDSA_SHA256
).getOrThrow()

// Open credentialAuthorizationUrl in a browser.
// After authorisation, the browser will redirect to authFlowRedirectionURI
// with a query parameter "code" containing the credential authorisation code.

Similar to service authorisation, the user will be redirected back to your authFlowRedirectionURI with a new code.

Use this code to complete the signing process:

val credentialAuthorizationCode = AuthorizationCode("credential-code")

val authorizedCredential =
    authorizedService.authorizeCredential(credentialAuthorizationCode).getOrThrow()

val signedDocuments = authorizedCredential.signDocuments().getOrThrow()

// Manipulate the signed documents
signedDocuments.forEach { (label, file) ->
    // Use the signed file
    val fileContent = file.readBytes()
}

You can also sign without explicitly calling authorizeCredential:

val signedDocumentsAlt = authorizedService.signDocuments(credentialAuthorizationCode).getOrThrow()

Source code

The source code is available on GitHub: eudi-lib-android-rqes-core.