Skip to content

Verified Identity with X.509 Certificates


In STRATO, users have cumbersome addresses, like 89bd47cd7cf6d516c2ff0a7ca2a12ec995b00a03. This is useful for machines but it is not particularly human readable. Furthermore it does not allow you to audit or verify who a user is in the real-world. Therefore any transactions on the blockchain are relatively anonymous without some type of real-world identification. To associate user addresses with a more meaningful, auditable identity, STRATO enables the usageof X.509 certificates.

A X.509 certificate is a digital file that binds a user's identity to a public key using a digital signature, employing widely accepted cryptographic standards. This protocol is widely for internet browser security and networking.

STRATO integrates X.509 Certificates directly on the blockchain, allowing a user's identity to be cryptographically verified and authenticated. This enables apps to use KYC functionality and participate in contracts with trusted partners.

Using the X.509 certificate protocol in STRATO allows for the following:

  • Verified identities associated with a user's STRATO address - including information such as organization, group in that organization, or country (e.g. “Acme", “Sales”, and "USA").
  • A database of address-name pairs to look up user identities by their STRATO address stored on-chain and accessible from the STRATO Cirrus API.
  • The ability to create complex contract logic in Solidity that is based on a user's identity, such as requiring someone to be part of "Acme" in order to execute a particular function.
  • A hierarchical structure where certificate authorities (CAs) issue organizations certificates. Properly certified organizations are part of the network. The certified organizations in turn issue certificates to their users. Properly certified users are part of the organization that gave them their certificate.

X.509 Certficate Basics

An X.509 Certificate is a digital record of an entity's identity. It can be thought of as digital ID issued by an authority. It contains 4 main sections: the Subject, the Issuer, the Public Key, and the Signature. There are also fields for metadata, however they are not relevant here.

Certificate Subject/Issuer

The subject contains information about who the certificate is for. For your own ID, you are the ID's subject. The issuer contains information about who gave the certificate to the subject. For your own ID, this would be like the government or your organization. An X.509 certificate contains the following fields for its subject and issuer (with abbreviated names):

  • Common Name (CN)
    • The subject's identifying common name (like "Ryan" or "Marjorie")
  • Organization (O)
    • The organization that the subject belongs to
  • Organization Unit (OU)
    • The department or sub-unit that the subject belongs to within their organization
  • Country (C)
    • The subject's country of operation/residence
  • Locality (L)
    • The subject's region of operation within their Country

A certificate that has the same subject/issuer is called "self-signed" and in most cases represents a entity that is a Certificate Authority.

Certificate Public Key

The Public Key section contains information about the subject's public-key. The subject must be in possession and control of the corresponding private-key.

  • Key Algorithm
    • The algorithm used to generate the key
  • Key Size
    • The bit size of the key
  • Public Key
    • The DER encoded public-key itself

Certificate Signature

The signature is the critical part of the certificate that enables it to be cryptographically secure and verified. Without this, there is no way to guarantee that the information in the certificate has not been falsified or tampered with. In most cases a certficate is signed with the private key of the issuer.

  • Signature Algorithm
    • Describes the hashing and signature algorithm used to sign this certificate
  • Signature
    • The actual signature of the certificate's data

The authenticity of a certificate may be checked at any time by performing a cryptographic signature verification on the certificate hash with the issuer's public-key.

Certificate Chains

The X.509 standard allows for certificate chains. This allows a valid certificate holder that is not neccessarily an authority to sign and create valid certificates for other users. This allows a chain of trust to be created from the main "Trust Anchor" to the final certificate holder. In a certificate chain, the certificates of all entities must be included in the certificate bundle. So a certificate may really be 1 or more individual ceritificates contained within a single file. This way, another party that requests and verifies the certificate always has the required information to identify an entity.

Certificate chain example:


In this certificate chain, the first certificate is the target certificate, the middle certificate is an intermediate certificate, and the last certificate is called the certificate anchor. A certificate chain is a list, the first cert is signed by the second, and the second cert is signed by the last one. For example, the cert anchor would be a CA, the intermediate cert would be an organization on the network, and the target cert would be a user within the organization of the intermediate cert.

We recommend this informative video on X.509 Certificates that explains the technology in-depth. Further discussion can be found in this Wikipedia article.

Verified Identities

STRATO ensures all user identities in X.509 certificates are Verified Identities. This is accomplished by following two-key industry standards not-dissimilar to those followed by internet browsers facilitating secure HTTPS connections through SSL.

Identity Verification

No ordinary certificate can be registered into the STRATO certificate database. Instead, certificates must contain info about a user's identity that is directly verified by BlockApps or another entity with a Verified Identity. For registering entities verified by BlockApps, BlockApps must receive certain credentials from a user about themselves and the organization to receive a valid certificate. This process is like a Certificate Signing Request when obtaining an SSL certificate for a website through a Certificate Authority like Commodo SSL or GeoCert SSL. Identities that are verified indirectly by other verified entities are fully responsible for the user identification process and BlockApps cannot be responsible for malicious data contained within a certificate verified in this manner.

Since STRATO account addresses are directly linked to a user's public-key (See STRATO Accounts), the certificate registration process guarantees that a certificate in the database is only registered to the address derived from the public-key contained within the certificate. This way user's can be sure that a user's identity is properly associated with their STRATO account.

Cryptographic Integrity

Certificates registered on STRATO networks are checked for their cryptographic authenticity to ensure an authority has issued the certificate. This is done through ECDSA signature verification on the data contained within the certificate. If the signature is not valid, then the certificate cannot be registered onto the blockchain.

A single certificate is valid if it is directly signed by a STRATO Certificate Authority (SCA).

Alternatively, users may also have a chain of certificates that are all signed by the next one in the list. A chain of certificates numbered [0... N] are valid if they meet the following criteria:

For each cert k in [0... N-1]:

  1. The issuer of cert k is the subject of cert k + 1
  2. The signature of cert k is verified by the public key of the subject in cert k + 1

Certificate N must be signed by a STRATO Certificate Authority.

See the Official X.509 Certificate Chaining Rules for the formal details.

Certificate chaining allows organizations to possess and register a single certificate signed by a SCA, and subsequently issue as many certificates to their users as needed.

STRATO Certificate Authorities

At the time of writing, the sole STRATO Certificate Authority is BlockApps Inc, which has the following self-signed certificate and corresponding public-key (DER encoded, PEM format):


Any X.509 certificate can be verified against the following BlockApps root public-key (DER encoded, PEM format):

-----END PUBLIC KEY-----

Obtaining a Certificate

In order to associate a STRATO account with a Verified Identity, first you will need a X.509 certificate from a STRATO Certificate Authority.

To get a certificate, contact a BlockApps representative with a request to create a new certificate for your organization or your organization's users. Please provide the following information:


  • Your organization name (O)
  • Your organization's country of operation (C)
  • An EC public-key for your organization generated on the secp256k1 curve. This key will be used to identify your organization on the blockchain and within certificates. See Generating Keys for creating keys directly using STRATO's built-in software, or there are several popular key-generation tools like the open-source CLI open-ssl or the Web3 JavaScript library.

For many individually signed certificates:

  • How many user certificates are required
  • For each user:
    • Common Name (CN)
    • Department/Sub-group (if applicable) (OU)
    • Country of operation (if different than your organization's) (C)
    • Their STRATO public-key (hexadecimal encoding)

After a BlockApps representative properly reviews and approves your request, they will issue your signed X.509 certificates and send them to you.

Creating User Certificates


This information is directed towards organization managers or STRATO node/network managers.

In order to make and issue certificates for your users, follow these steps to easily create new certificates with STRATO's built-in tools:

1. Transfer files to the STRATO Docker container:

  • Your SCA-signed X.509 certificate in .pem file format.
  • Your organization's private-key in .pem file format. (Note STRATO does not manage organizations' private keys)
docker cp ./certs/orgCert.pem strato_strato_1:var/lib/strato/
docker cp ./keys/orgPriv.pem strato_strato_1:var/lib/strato/

2. Create X.509 Subject files for each user:

In a text editor, create a subject_1.json file (file name does not matter).

touch ./subject_1.json

Create a JSON object containing the following identity properties of the soon-to-be-made certificate:

  "commonName": "Rebekah",
  "organization": "Standard Oil",
  "organizationalUnit": "Product",
  "country" : "USA",
  "pubKey" : "0423..50d"

An existing STRATO user's public-key may be retrieved using the /key endpoint. See the STRATO API Basics - Getting an Account's Address for more information.

Repeat this process for every user in your organization that you wish to have a Verified Identity.

3. Copy each file into the STRATO container

docker cp ./subject_*.json strato_strato_1:var/lib/strato/

4. Create the X.509 Certificate .pem file(s)

Enter the STRATO Docker container

docker exec -it strato_strato_1 bash

Run the STRATO x509-generator command:

x509-generator --subject=subject_1.json --key=orgPriv.pem --issuer=orgCert.pem

This command will use the data in each provided file to create a new X.509 certificate in the file outputCert.pem. Currently, certificate serial numbers are randomly generated and the certificate is valid for 1 year after the time of creation.

Upon successful certificate creation, the command will output the following message:

Done. Cert was written to outputCert.pem

See below in the X.509 Generator section for a full reference of each command option.

Use these files to register users' identities on STRATO.

A X.509 certificate does not contain sensitive data, but should be stored with write permissions removed so the information does not get accidentally corrupted. Organizations may still choose to restrict access to these certificates based on their own role and permission management practices. Certificates should be stored using your organization's credential storage best practices.


A user may use any tool to create X.509 certificates, however keep in mind that they must follow these guidelines:

  • Signed using SHA256 with an EC private-key from the secp256k1 curve. ("ecdsa-with-sha256".)
  • Encoded using DER
  • Written to a .pem file
  • Subject public-keys must also be in DER/.pem encoding

Registering Identities on STRATO

Once you have obtained your X.509 certificate files, you must register them onto your STRATO blockchain network. These certificates will be provided to you in the .pem file format, DER encoded.

Certificate registration can only occur through a BlockApps Certificate Registry Smart Contract. This contract must be uploaded to your STRATO network directly by a BlockApps representative, so contact us to make sure this Dapp is posted to your network. This contract will be posted by the BlockApps Network Manager when setting up your STRATO network for the first time, and they will distribute the Dapp's address to the network's users.

If you are unsure about the address of your network's Certificate Registry Dapp, you may also query the network's Cirrus API to get its contract address:

curl -X GET \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json" \


    "address": "24c6003021471df20530ba4AE973527A8D2F4385"

const rootAddress = '74f014FEF932D2728c6c7E2B4d3B88ac37A7E1d0'
const query = {
  select: 'address',
  transaction_sender: `eq.${rootAddress}`,
  order: 'block_number.desc',
  limit: 1

const newOptions = {
const contract = {
  name: "CertificateRegistry"

const rows = await, contract, newOptions)

const certDappAddress = rows[0].address

const rows = [
    address: "24c6003021471df20530ba4AE973527A8D2F4385",

Once you have retrieved the Dapp address, you may call the following exposed registration function: registerCertificate.

registerCertificate signature:

function registerCertificate(string _cert) returns (account, int);


All certificate strings sent to STRATO must have the newline character stored in the .pem file replaced with the \n newline escape sequence, so it can be read as a single string.

This function must be used when registering any X.509 certificate.

The certificate being registered must be valid.

If you are an organization manager and you are registering a user's cert you would call this function with a string containing every certificate starting with the user's certificate to your organization's certificate. Even if a certificate in the chain is already registered on STRATO, it still must be included in the list.

The function returns the account that the certificate were registered to (the last one in the case of registerCertificate), and a relevant HTTP status code depending on the result of the function call: 200 for success, 400 for an error.

Transaction example:

curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "txs": [
        "payload": {
          "contractName": "CertificateRegistry",
          "contractAddress": "24c6003021471df20530ba4AE973527A8D2F4385",
          "method": "registerCertificate",
          "args": {
            "_cert": <certificate>
          metadata: {
            "VM": "SolidVM"
        "type": "FUNCTION"
    "txParams": {
      "gasLimit": 32100000000,
      "gasPrice": 1
  }' \


    "status": "Success",
    "hash": "9353c6a06f736f9ea1f29edd4f9dedc4b99c3e671af8004c115c90687b41e6c1",
    "txResult": {...},
    "data": {
      "tag": "Call",
      "contents": [ "<registered-account>", "200" ]
const fs = require('fs')
const path = require('path')

// JS already replaces newlines with '\n'
const myCertificate = fs.readFileSync(path.resolve(process.cwd(), 'certs', 'myCert.pem')).toString()

const call = {
  contract: {
    name: "CertificateRegistry",
    address: "24c6003021471df20530ba4AE973527A8D2F4385",
  method: "registerCertificate",
  args: {
    _cert: myCertificate

const callOptions = {
  config: {
    VM: 'SolidVM',
    isAsync: true

const callResults = await, call, callOptions)



It is recommended that all function calls to the Certificate Registry Dapp happen in independent transactions and not from within other contracts.

Accessing Verified Identities

Certificates in SolidVM

To obtain information about a user's Verified Identity X.509 certificate, SolidVM ^3.0 has a built-in function getUserCert. This can be used to get the identity information of a user based on their STRATO address.

getUserCert function signature:

function getUserCert(account _account) returns (mapping(string => string));

The function returns the subject information for this user's certificate as key/values:

  • commonName
    • The account's registered Common Name (CN)
  • organization
    • The account's registered Organization (O)
  • group
    • The account's registered Organizational Unit (OU)
  • country
    • The account's registered Country (C)
  • publicKey
    • The account's registered Public Key
    • DER encoded
    • This is identical to the account's public key in STRATO's Vault
  • certString
    • The DER encoded string of the user's X.509 certificate

If an account does not have a verified identity on STRATO, the function will return a mapping with empty values for each key.

pragma solidvm 3.2;
contract IdentityFetcher {

  constructor() {}

  function getIdentity(address _addr) returns (string, string, string, string, string, string) {
    account myAccount = account(_addr, "main");
    myCommonName   = getUserCert(myAccount)["commonName"];      // Get the common name
    myCountry      = getUserCert(myAccount)["country"];         // Get the country
    myOrganization = getUserCert(myAccount)["organization"];    // Get the organization
    myGroup        = getUserCert(myAccount)["group"];           // Get the group
    myPublicKey    = getUserCert(myAccount)["publicKey"];       // Get the public-key
    myCertificate  = getUserCert(myAccount)["certString"];      // Get the cert string
    return (myCommonName, myCountry, myOrganization, myGroup, myPublicKey, myCertificate);

BlockApps Public Certificate Registry List

When a user's certificate is registered on STRATO, their identity information is put in the STRATO Certificate Registry List, the database of contracts representing all Verified Identities on the network.

User's may access this by querying the Cirrus API for the CertificateRegistry-Certificate table:


curl -X GET \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json" \


    "userAddress": "1471df2240030205327A8D2F438c60ba4AE97355", 
    "commonName": "Rebekah",
    "organization": "Standard Oil",
    "group": "Product",
    "country": "USA",
    "publicKey": "<public-key>",
    "certString": "<certificate>"
const contract = {
  name: "CertificateRegistry-Certificate"

const rows = await, contract, options)


const rows = [
    userAddress: "1471df2240030205327A8D2F438c60ba4AE97355", 
    commonName: "Rebekah",
    organization: "Standard Oil",
    group: "Product",
    country: "USA",
    publicKey: "<public-key>",
    certString: "<certificate>"

Using Verified Identities

Once a user has a verified identity on-chain, this allows them to have access to useful STRATO features like Cirrus Table namespacing based on Organization, extra built-in tx properties, and access to Smart Contracts that require KYC or specific organization membership.

All additional features require SolidVM version 3.2 or higher to be accesible.

Transaction Built-in Properties

Any transaction posted by a user with a Verified Identity will extend the built-in tx member to contain the following properties:

  • tx.username
    • The transaction origin's registered Common Name (CN)
  • tx.organization
    • The transaction origin's registered Organization (O)
    • The transaction origin's registered Organizational Unit (OU)

If there is no Verified Identity of the transaction's origin, then each property will default to the empty string.

Organization-level Table Namespacing

When a user with a Verified Identity posts a smart contract, the contract will automatically be associated with their organization in the Cirrus database. This allows for multiple organizations to create contracts with similar names without creating confusion in the database, or naming collisions.

For example, if BlockApps and an associated company, Standard Oil, both post a "HelloWorld" contract on the same network, the database query results in Cirrus will show separate storage tables for BlockApps-HelloWorld and StandardOil-HelloWorld. This means any version changes to either contract will be kept in a separate table of the database.

A look at the schema inside the Cirrus database would look something like this:

                List of relations
 Schema |       Name                                    |   Type    |  Owner   
 public | StandardOil-HelloWorld                        | table     | postgres
 public | BlockApps-HelloWorld                          | table     | postgres
 public | BlockApps-HelloWorldFactory-HelloWorld        | table     | postgres

Additionally, if BlockApps uses a factory contract (See Common Smart Contract Design Patterns) to create other contracts, the contract tables will be namespaced by both the organization name AND the factory name. If BlockApps uses a "HelloWorldFactory", the query results would show the created contracts as BlockApps-HelloWorldFactory-HelloWorld.

                List of relations
 Schema |       Name                                    |   Type    |  Owner   
 public | BlockApps-HelloWorldFactory-HelloWorld        | table     | postgres


Cirrus API queries on contracts that are namespaced with a prefix must include the full (namespaced) contract name, such as "BlockApps-HelloWorld" or "BlockApps-HelloWorldFactory-HelloWorld" to retrieve the correct contract.


In STRATO v7.5, the table name separator was changed from a ":" (colon) to a "-" (dash).

Requiring Organization Membership

Smart Contract Application designers may now restrict functionality to user's of a specific organization:

pragma solidvm 3.2;
contract ProjectManager {

  int projectCounter = 1;
  string ownerOrg;

  constructor() {
    ownerOrg = tx.organization;

  function createProject() {
    require(tx.organization == ownerOrg, "You must be a member of ownerOrg to call this function");
    require( == "Product", "You must be in the Product Department to call this function");

Certificate Utilities

SolidVM also offers built-in utility functions for certificates that allows for easy parsing and verification.

Parsing Certificates

The parseCert function allows contract's to read the subject data of a X.509 certificate without registering it to the STRATO identity database. This function is available in SolidVM ^3.0.

The parseCert function has a similar signature as getUserCert:

function parseCert(string _cert) returns (mapping(string => string));

The function returns the subject information for this user's certificate as key/values:

  • commonName
    • The certificates' Common Name (CN)
  • organization
    • The certificates' Organization (O)
  • group
    • The certificates' Organizational Unit (OU)
  • country
    • The certificates' Country (C)
  • publicKey
    • The certificates' DER encoded Public Key
  • certString
    • The DER encoded string of this X.509 certificate

Here is a simple example:

pragma solidvm 3.2;
contract identityParser {

  string myCommonName = "";
  string myCountry = "";
  string myOrganization = "";
  string myGroup = "";
  string myPublicKey = "";
  string myCertificate = "";

  constructor(string _cert) {
    mapping(string => string) certificate = parseCert(_cert);
    myCommonName   = parseCert(certificate)["commonName"];      // Get the common name
    myCountry      = parseCert(certificate)["country"];         // Get the country
    myOrganization = parseCert(certificate)["organization"];    // Get the organization
    myGroup        = parseCert(certificate)["group"];           // Get the group
    myPublicKey    = parseCert(certificate)["publicKey"];       // Get the public-key
    myCertificate  = parseCert(certificate)["certString"];      // Get the cert string

Certificate Verification

The verifyCert built-in function allows contracts to verify a X.509 certificate's signature with a given public-key using ECDSA on the secp256k1 curve. This allows user's to bypass the complex parsing and extraction of certificate data. This function is available in SolidVM ^3.2.

verifyCert signature:

function verifyCert(string _cert, string _pubkey) returns (bool);


  • string _cert
    • A DER encoded X.509 Certificate or X.509 Certificate Chain. Newlines replaced with '\n'.
  • string _pubkey
    • A DER encoded EC public key. Newlines replaced with '\n'

The function returns true if the certificate is valid using the certificate validity rules as mentioned above. The provided public key is used as the final Trust Anchor to verify the last certificate against.


pragma solidvm 3.2;
contract CertVerifier {

  bool isValid;

  constructor(string _cert, string _pubkey) {
    isValid = verifyCert(_cert, _pubkey);

Key/Certificate Command Reference

The following are commands available in the strato_strato_1 container:


The x509-generator command creates new certificate files for use in STRATO. It takes the following arguments:

  • subject, s (required)
    • The name of the .json file containing the certificate subject information
    • Must exclusively have the following keys:
      • commonName : string
      • organization : string
      • organizationalUnit : string
      • country : string
      • pubKey : string
    • The public-key must be hexadecimal encoded
  • key, k (required)
    • The name of the .pem file containing the EC private-key used to sign this certificate
    • The private-key must be DER encoded
  • issuer, i (optional)
    • The name of the .pem file of this certificate's issuer.
    • The certificate must be DER encoded
    • When omitted, the certificate being created will be self-issued.
    • When specified, the issuer certificate will be appended to the new certificate, creating a certificate chain.
  • output, o (optional)
    • The path of the output file to write the certificate to.
    • Defaults to "./outputCert.pem"

This command creates a .pem format, DER encoded X.509 certificate to the output file.

Successful execution results in the following output:

Done. Cert was written to outputCert.pem


The x509-keygen is a simple utility that makes a new EC key for you compatible with STRATO and the x509-generator.

This command takes no arguments.

This command creates a .pem format, DER encoded EC privatek-key in the priv.pem file in the current directory. It also creates a keydata.json file that contains the hex-encoded public and private key pairs, as well as the STRATO address that would be used for this key.

Successful execution results in the following output:

writing keydata to keydata.json
writing encoded private-key to priv.pem


The x509-info is a command that allows users to read a certificate .pem file in a human-readable format.

It takes the following argument:

  • cert, c:
    • The name of the .pem file containing the certificate to be read

Successful execution will output the following:

IIssuer {issCommonName = "Admin", issOrg = "BlockApps", issUnit = Just "Engineering", issCountry = Just "USA"}
   R: "6f16d0f0d2e40c47b699627fa9ae2737cb7129cf06f51dbb658014bb3d79cc5d"
   S: "3c47450445065b177a8952de40d1915fd99981d25fd93e322f337f3780e177c7"
Signature (DER Encoding): "304402203c47450445065b177a8952de40d1915fd99981d25fd93e322f337f3780e177c702206f16d0f0d2e40c47b699627fa9ae2737cb7129cf06f51dbb658014bb3d79cc5d"
Certificate Hash: "68410110452c1179af159f85d3a4ae72aed12101fcb55372bc97c5108ef6e4d7"
Subject {subCommonName = "Admin", subOrg = "BlockApps", subUnit = Just "Engineering", subCountry = Just "USA", subPub = PublicKey "03521251e31fb06625fec592b69bfa70378d1cbc24b4500ed9d0307bb27cb96673"}
Subject Address: 74f014fef932d2728c6c7e2b4d3b88ac37a7e1d0
TRUE. This cert was signed by BlockApps

The last line is a useful indication of whether this certificate was directly signed by the BlockApps root key.


This command-line tool was introduced in STRATO v7.6.