Skip to content

Tutorial: Sign Code Using the Demo Server

This tutorial shows how to sign code using the Cryptera Keyserver trial environment.
You will authenticate as a client, request a signing operation, obtain approval, and retrieve a valid signature.

The tutorial uses: - The REST API - Example users and keys available on the trial server - Standard command-line tools: curl, jq, openssl, and g++ (can be omitted)

This tutorial is intended for new users and CI/CD engineers who want to understand the complete signing workflow.


Uses Internal Authserver

This tutorial uses the Keyserver’s built-in internal Authserver for demonstration purposes only.
Do not use the internal Authserver in production.
Production deployments must authenticate using an external OAuth2/OIDC provider such as
Azure Entra ID, GitLab OIDC, GitHub Actions OIDC, or Keycloak.

Trial vs Production Authentication

The internal Authserver is intended for evaluation and testing only.
Production environments must rely on an external identity provider.

Prerequisites

This tutorial assumes:

  • While most Linux distributions should work, this tutorial is based on Ubuntu 24.04
  • You have the following tools installed:
sudo apt install curl jq openssl g++

Replace apt with your platform's package manager if needed.


Environment Setup

Set the base URLs for your trial server:

export KEYSERVER="https://keyserver.<UNIQUE-ID>.cryptera-security.io"
export AUTHSERVER="https://authserver.<UNIQUE-ID>.cryptera-security.io/oauth2/token"

Step 1: Authenticate as a Client

In this tutorial, you will authenticate as fw-client, a client subject allowed to request signing operations. The trial environment uses same predefined password for all users, refer to your getting started email.

Create a Base64-encoded credential string:

export AUTH_CREDS=$(echo -n fw-client:password | base64)

Request an OAuth2 access token:

export TOKEN=$(
  curl -sS --tlsv1.3 \
    -X POST \
    -H "Authorization: Basic $AUTH_CREDS" \
    -d "grant_type=client_credentials" \
    $AUTHSERVER | jq -r .access_token
)

Verify:

echo $TOKEN

If the token prints, authentication succeeded.


Step 2: Choose a Key for Signing

The trial server provides several predefined signing keys.
You can view and manage these keys at Signing Key Admin.

For demonstration, four preconfigured keys are available:

Key Algorithm Hash Approvers Required
key1 ECDSA SHA256 0
key2 ECDSA SHA384 1
key3 ECDSA SHA512 2
key4 RSA SHA256 1

For this tutorial, we use key2, which requires one approver.
This demonstrates the approval workflow clearly.

Set environment variables:

export KEYID="key2"
export HASH_ALG="-sha384"

Fetching the certificate allows you to verify the signature later.

curl -sS --tlsv1.3 \
  -H "Authorization: Bearer $TOKEN" \
  "$KEYSERVER/api/keys/$KEYID" | jq -r .certificate \
  > certificate.pem

Step 4: Build and Hash a Test Binary

Create a simple C program:

echo '#include <stdio.h>
int main() {
    printf("Hello World!\n");
    return 0;
}' > hello.cpp

Compile it:

g++ hello.cpp -o hello.bin

Hash the binary using the required algorithm:

export HASH_TO_SIGN=$(openssl dgst $HASH_ALG hello.bin | awk '{print $2}')

Step 5: Request a Signing Operation

A signing operation defines:

  • Which key will be used
  • How many times the key may be used
  • How long the operation is valid
  • A human-readable description

Request an operation:

export OPERATION_ID=$(
  curl -sS --tlsv1.3 \
    -X POST "$KEYSERVER/api/operations" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
          "validms": 60000,
          "description": "Signing Hello World example binary",
          "new-keyusagerestrictions": [
            { "keyid": "'"$KEYID"'", "maxusagecount": 1 }
          ]
        }' | jq -r .id
)

Verify:

echo $OPERATION_ID

This operation now awaits approval.


Step 6: Approve the Operation (via the UI)

  1. Open your browser.
  2. Navigate to:
    https://keyclient.<UNIQUE-ID>.cryptera-security.io/approvaladmin
  3. Log in as an approver, e.g.:
    username: approver-fw2
    password: <PASSWORD>
    
  4. Locate the pending operation with your OPERATION_ID.
  5. Review the details.
  6. Click Approve.

Once approved, the operation becomes active.


Step 7: Request the Signature

With the operation now approved, request the signature:

export SIGNATURE=$(
  curl -sS --tlsv1.3 \
    -X POST "$KEYSERVER/api/signorders" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
          "operationid": "'"$OPERATION_ID"'",
          "keyid": "'"$KEYID"'",
          "inputdata": "'"$HASH_TO_SIGN"'",
          "inputformat": "hex",
          "inputpadding": "none",
          "signatureformat": "asn1",
          "description": "Hello World example signing"
        }' | jq -r .result
)

Print the signature:

echo $SIGNATURE

This value is ASN.1-encoded and hex-formatted.


Step 8: Verify the Signature

Convert the signature to binary:

echo $SIGNATURE | xxd -p -r > hello.bin.sig

Extract public key from certificate:

openssl x509 -pubkey -noout -in certificate.pem > pubkey.pem

Verify:

openssl dgst $HASH_ALG -verify pubkey.pem -signature hello.bin.sig hello.bin

Expected output:

Verified OK

Step 9: Modify Binary to fail verification

To trigger a failed verification step, the binary can be modify to no longer match what was signed.

Modify the program:

echo '#include <stdio.h>
int main() {
    printf("Hello _Modified_ World!\n");
    return 0;
}' > hello.cpp

Compile it:

g++ hello.cpp -o hello.bin

And try to verify again:

openssl dgst $HASH_ALG -verify pubkey.pem -signature hello.bin.sig hello.bin

Completion

You have now:

  • Authenticated as a Keyserver client
  • Requested an operation using a key requiring approval
  • Approved the operation via the UI
  • Performed a signing operation using the REST API
  • Verified the resulting signature using OpenSSL

This completes the basic signing workflow and establishes the foundation for integrating signing into CI/CD systems.