Digital Signatures

Digital signatures are another form of data authentication supported in the PowerAuth protocol. The PowerAuth Mobile SDK provides a unified interface for digital signatures, allowing you to compute or verify digital signatures or MAC tokens.

Signature Key Identifiers

To compute or verify a signature, you must specify the key used for the operation. The following basic key categories are available:

  • “master” public keys are used to verify data signed by the server. These keys can be used with or without an activation present in the PowerAuthSDK instance.
  • “server” public keys are personalized keys uniquely associated with an activation. You can use these keys to verify data signed by the server.
  • “device” private and public keys are stored locally on the device and associated with an activation. You can use these keys to sign data and to verify previously signed data.
  • “MAC” keys are symmetric keys used to verify MACs calculated by the server.

The table below lists all available key identifiers defined in the PowerAuthSignatureKeyId enumeration and operations supported with the identifier:

Key identifier Key Type Signature Activation Sign Verify Description
master Any Any or Hybrid No No Yes Use all available “master” public keys for signature verification.
master_EC EC ECDSA No No Yes Use only the EC-based “master” public key for ECDSA signature verification.
master_ML_DSA ML-DSA ML-DSA No No Yes Use only the ML-DSA-based “master” public key for ML-DSA signature verification.
server Any Any or Hybrid Yes No Yes Use all available “server” public keys for signature verification.
server_EC EC ECDSA Yes No Yes Use only the EC-based “server” public key for ECDSA signature verification.
server_ML_DSA ML-DSA ML-DSA Yes No Yes Use only the ML-DSA-based “server” public key for ML-DSA signature verification.
device Any Any or Hybrid Yes Yes Yes Use all available “device” private and public keys for signature computation or verification.
device_EC EC ECDSA Yes Yes Yes Use only the EC-based “device” private and public key for ECDSA signature computation or verification.
device_ML_DSA ML-DSA ML-DSA Yes Yes Yes Use only the ML-DSA-based “device” private and public key for ML-DSA signature computation or verification.
macPersonalized MAC KMAC Yes No Yes Use the KMAC-based symmetric key for MAC verification.

If you’re interested in more technical details, such as the exact algorithms used for digital signatures, see the Digital-Signatures.md document.

Signature Key Availability

The availability of key types depends on the selected PowerAuth Algorithm:

  • “EC” keys are always available.
  • “ML_DSA” keys are available only if the EC_P384_ML_L3 or EC_P384_ML_L5 algorithms are used.
  • “MAC” keys are available for all algorithms except LEGACY_P256.

If you select a key without specifying its exact type (for example, .master), it may lead to multiple key selections. For example, the EC_P384_ML_L3 and EC_P384_ML_L5 algorithms use two keys for each key category. The format of hybrid signatures is not yet standardized; therefore, the PowerAuth Mobile SDK supports such key identifiers only in JWS functions. JWS, by design, supports multiple keys in signatures.

Sign Data With Device Private Key

To compute a digital signature using an asymmetric device private key, request user credentials (such as password or PIN), specify the signing key (always use the “device” key), and use the following code:

// 2FA authentication — uses the device-related key and user PIN code
let auth = PowerAuthAuthentication.possessionWithPassword(password: "1234")
// Specify the key to sign with. In this case, device_ML_DSA is used,
// and therefore an ML-DSA signature will be produced.
let signingKey = PowerAuthSignatureKeyId.device_ML_DSA
// Unlock the device private key after successful authentication and perform data signing.
powerAuthSDK.calculateDigitalSignature(authentication: auth, forData: data, withKey: signingKey) { signature, error in
    if let signature {
        // Send data and signature to the server
    } else {
        // Authentication or network error
    }
}

If the PowerAuthSDK instance is not configured for the legacy mode (that is, the algorithm is not LEGACY_P256), you can also use biometric authentication to access the device private key.

Create JSON Web Signature with Device Private Key

The asymmetric private key signatures described in the previous chapter can be used to create a JSON Web Signature (JWS). The example below demonstrates how to construct a JWS from generic data:

// 2FA authentication — uses the device-related key and user PIN code
let auth = PowerAuthAuthentication.possessionWithPassword(password: "1234")
// Unlock the device private key after successful authentication and perform data signing.
// - The dataType parameter is added to the JWS Protected Header under the "typ" key.
//   A nil parameter means that no "typ" value is included in the header.
// - The compact parameter determines whether the output is a JWS (compact = false) or JWT (compact = true).
powerAuthSDK.calculateJwsSignature(authentication: auth, forData: data, dataType: nil, compact: false, withKey: .device_ML_DSA) { jws, error in
    if let jws {
        // jws contains a JSON string with the JWS object
    } else {
        // Authentication or network error
    }
}

The following example demonstrates how to construct a signed JWT:

// Construct claims dictionary
let claims = [
    "sub": "user-id",
    "first_name": "John",
    "last_name": "Appleseed"
]
guard let claimsData = try? JSONSerialization.data(withJSONObject: claims) else {
    fatalError() // JSON serialization error
}
// 2FA authentication — uses the device-related key and user PIN code
let auth = PowerAuthAuthentication.possessionWithPassword(password: "1234")
// Unlock the secure vault, fetch the private key, and perform data signing
powerAuthSDK.calculateJwsSignature(authentication: auth, forData: claimsData, dataType: "JWT", compact: true, withKey: .device_ML_DSA) { jws, error in
    if let jws {
        // jws contains the JWT string
    } else {
        // Authentication or network error
    }
}

Verify Server-Signed Data

This task is useful when you receive arbitrary data from the server and need to verify that it was indeed issued by the server. The PowerAuthSDK provides a high-level method for validating data and its associated signature:

do {
    try powerAuthSDK.verifyDigitalSignature(signature: signature, forData: signedData, withKey: .server_ML_DSA)
    print("Signature is valid")
} catch let error as NSError where error.domain == PowerAuthErrorDomain {
    if error.powerAuthErrorCode == .wrongSignature {
        print("Signature is invalid")
    } else {
        // other cause of failure
    }
}

Verify Data Encoded in QR Code

In cases where you need to verify the authenticity of a QR code created on the server and authenticated with a personalized MAC key (for example, when authenticity is bound to an activation), use the .macPersonalized key identifier. For example:

do {
    try powerAuthSDK.verifyDigitalSignature(signature: signature, forData: signedData, withKey: .macPersonalized)
    print("MAC is valid")
} catch let error as NSError where error.domain == PowerAuthErrorDomain {
    if error.powerAuthErrorCode == .wrongSignature {
        print("MAC is invalid")
    } else {
        // other cause of failure
    }
}

Verify JSON Web Signature

To verify a JSON Web Signature (JWS) created on the server, use the following code:

do {
    try sdk.verifyJwsSignature(signature: jws, compact: false, strict: true, withKey: .server)
} catch let error as NSError where error.domain == PowerAuthErrorDomain {
    if error.powerAuthErrorCode == .wrongSignature {
        // signature is not valid
    } else {
        // other cause of failure
    }
}

Explanation of verifyJwsSignature function parameters:

  • signature - A string containing JWS or JWT signed data.
  • compact - If true, the input string is a compact JWT; otherwise, a full JWS object is expected.
  • strict — If true, all selected keys must successfully verify their corresponding signatures. If false, verification succeeds when at least one provided key matches a valid signature; however, invalid or mismatched signatures still result in an error. It is generally recommended to use true, unless you have a specific reason to reduce the strict verification.
  • key- The identifier of the key used for verification. Be aware, that this API doesn’t support .macPersonalized key.

The compact (JWT) format encodes only a single signature, so it is recommended to specify the exact key type (EC, ML-DSA, etc.) for verification. If a generic key identifier is provided (such as .server), the function may fail when the current algorithm results in multiple key selections. You can relax this behavior by setting the strict parameter to false, but this is generally not recommended. In non-strict mode, an attacker could potentially remove or replace a stronger PQC signature with a weaker one without detection.

Creating Certificate Signing Request

The PowerAuth SDK can create a Certificate Signing Request (CSR) that can be used to request an X.509 certificate from a Public Key Infrastructure (PKI). The CSR contains a device public key generated by the SDK and is signed with the activation-bound private key.

The created CSR is returned in PEM format, including the -----BEGIN CERTIFICATE REQUEST----- and -----END CERTIFICATE REQUEST----- lines and newline characters (\n).

To create a CSR, use the following code:

let authentication = PowerAuthAuthentication.possessionWithPassword(password: "1234")
let keyIdentifier = PowerAuthSignatureKeyId.device_ML_DSA

powerAuthSDK.createCertificateSigningRequest(
    authentication: authentication, // authentication object
    distinguishedNames: [ // subject's distinguished names (DN)
        "CN" : "wultra.com",
        "O"  : "Wultra",
        "C"  : "CZ"
    ],
    subjectAltNames: [ // subject's alternative names (SAN)
        "IP: 192.168.1.10",
        "email: [email protected]"
    ],
    keyIdentifier: keyIdentifier // Key Identifier
) { csr, error in
    if let csr {
        print("CSR: \(csr)")
        // Use the CSR
    } else {
        // Handle error
    }
}

If the PowerAuthSDK instance is not configured for the legacy mode (that is, the algorithm is not LEGACY_P256), you can also use biometric authentication to create CSR.

Getting Device Public Keys

Use the following code to retrieve device public keys associated with the activation:

let allKeys = try powerAuthSDK.exportDevicePublicKeys(format: .der)
if let publicKey = allKeys.first(where: { $0.keyType == .EC }) {
    print("EC key algorithm: \(publicKey.keyAlgorithm)")
    print("  X.509 key data: \(publicKey.keyData.base64EncodedString())")
}
if let publicKey = allKeys.first(where: { $0.keyType == .ML_DSA }) {
    print("ML-DSA key algorithm: \(publicKey.keyAlgorithm)")
    print("      X.509 key data: \(publicKey.keyData.base64EncodedString())")
}

Available format specifiers:

  • .der - The public key is exported in binary X.509 (DER) format.
  • .raw - The raw key format depends on the key type:
    • EC keys: The output is ASN.1 encoded, as defined in ANSI X9.63.
    • ML-DSA keys: The output contains the raw public key obtained via OpenSSL’s EVP_PKEY_get_raw_public_key().
Last updated on Oct 23, 2026 (12:47) Edit on Github Send Feedback

docs.experimental

PowerAuth Mobile SDK