Skip to content

X.509 Certificates as Identity


In STRATO, users have cumbersome addresses, like 89bd47cd7cf6d516c2ff0a7ca2a12ec995b00a03. This is useful for machines but it is not particularly human readable. To associate user addresses with a more meaningful identity, we can use X.509 certificates.

A X.509 certificate is a mechanism to bind a user identity to a public key using a digital signature, employing widely accepted cryptography standards. This technology protocol is already used widely for internet browser security. We recommend this informative video on X.509 Certificates that explains the technology in-depth. Further discussion can be found in this Wikipedia article. The X.509 certificate does not contain sensitive data, but should be stored with write permissions removed. Organizations may still choose to restrict access to these certificates based on their own role and permission management practices.

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

  • A database of address-name pairs where we can look up names by their address.
  • The ability to assign an identity to each address and to include information such as country, organization, or group in that identity (e.g. “USA”, “BlockApps”, and “sales”).
  • The ability to create conditions in Solidity that are executed based on identity, such as requiring someone to be part of “BlockApps” in order to execute a particular function.
  • A hierarchical structure where organizations can be given the ability to create and manage users under their organization.

Getting Started

In order to associate an identity with an X.509 certificate, first you will need a certificate.

Requirements for certificates:

  • DER encoding
  • .pem file format
  • ECDSA with SHA256 for signing
  • Keys generated with the secp256k1 elliptic curve

The following fields are used to identify the subject of the certificate, and these are established at the time the certificate is generated:

  • commonName (string)
  • organization (string)
  • unit/department (string, optional)
  • country (string, optional)
  • pubkey (the base16 encoded, DER encoded secp256k1 public-key)


Currently, the tools to generate a certificate are designed for node administrator use. If you are not a node administrator or if you already have a certificate of the appropriate type,then go ahead and skip to the section on Registering a Certificate.

Generating a Certificate

If you need a new X.509 certificate, STRATO has some internal tools to help you generate one.

Create User Key Pair

First, generate a public-private key pair for your user. You can do this with the /key endpoint, as follows:

curl -X POST \
   -H "Authorization: Bearer <token>" \

This key currently is generated with the same protocols that STRATO uses for generating your public-key, but currently, these keys are used for X.509 certificates only (and not stored in the vault-wrapper).

Faucet (Add Value) to an Account

Next, if gas is enabled on your STRATO network, you will need to faucet (add value) to your an account. An account must have a positive token balance to pay the gas price for each transaction. You can find instructions on how to faucet in this section of the developer documentation.

If your network does not have gas enabled, skip this and go on to the next step.

Command Line Utilities for X.509 Certificate Generation

To generate X.509 certificates we have a set of two command line utilities--x509-keygen and x509-generator. To use these tools, you will need have a STRATO container running.


x509-keygen is a simple utility that makes a new key for you. This will be necessary for certificate creation. To run, use this command:

docker exec -it strato_strato_1 bash

This will output:

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

The new priv.pem file and the public-key will be used with x509-generator.


This utility will generate the X.509 certificate itself. To use the generator, we will need to execute these inside the docker container (or, if you have exited, you can re-enter the container with docker exec -it strato_strato_1 bash. If you list the files in your directory using ls, you should still see the priv.pem file that we generated in the previous step.

Before we generate the certificate, we need to do one more step. We need to make a JSON file representing the identity properties of the soon-to-be-made certificate. Using the following example, create a new file with the properties for your user, and save it as subject.json. It will be easier to do this outside of the docker container, so exit the docker container.

  "commonName": "Bob",
  "organization": "Blockapps",
  "organizationUnit": "engineering",
  "country" : "USA",
  "pubKey" : "0423..50d"

Copy this file into the container using docker cp subject.json strato_strato_1:<filepath>. While still inside the docker container, run the utility with the following command:

x509-generator --subject=subject.json --key=priv.pem

It will output: Done. Cert was written to outputCert.pem

Our certification will be saved to outputCert.pem and should look something like this:


You will need to use this certificate later on in your smart contract, so you may want to copy the contents of your file (including the BEGIN/END CERTIFICATE lines) to a clipboard or your code editor for reference before exiting the docker container.


This certificate will be used to associate a user with your group or organization and would be best stored in a more permanent location, in accordance with your organization's certificate storage practices.

Registering a Certificate

Now that we have created our X.509 certificate, we need to let STRATO know about it. We do that by using a SolidVM built-in function, registerCert() within a smart contract.

To use this function, you will need to include pragma solidvm 3.0; at the top of your contract to let STRATO know the proper SolidVM feature set to use. You will also need to replace the newline characters within your certificate with a \n to form one continuous string.

Our sample contract could look something like this:

pragma solidvm 3.0;
contract identityRegistrar {

   constructor() {
       registerCert(tx.origin, certificate);   // Register the certificate to tx.origin's address

Because the registerCert() function is in the constructor, your certificate will be registered as soon as this contract is posted. However, if you wish to register your X.509 certificate at a later point, you could create the function outside of the constructor and call it through a function call instead.


Registering X.509 certificates through contracts on private chains is not supported, and doing so will result in an error.

Retrieving Certificate Information

In the event that you need to obtain information about a user's X.509 certificate, SolidVM has another built-in, getUserCert() that can be used to get any of the identity properties of a user.

To use, here is an example.

pragma solidvm 3.0;
contract identityFetcher {
   address myAddress = tx.origin;

   string username = "";
   string org = "";
   string group = "";

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

   constructor() {

       username = tx.username;         // Get tx.origin's common name
       org      = tx.organization;     // Get tx.origin's organization
       group    =;            // Get tx.origin's group

       myCommonName   = getUserCert(myAddress)["commonName"];      // Get the common name
       myCountry      = getUserCert(myAddress)["country"];         // Get the country
       myOrganization = getUserCert(myAddress)["organization"];    // Get the organization
       myGroup        = getUserCert(myAddress)["group"];           // Get the group
       myPublicKey    = getUserCert(myAddress)["publicKey"];       // Get the public-key
       myCertificate  = getUserCert(myAddress)["certString"];      // Get the cert string
Again, the getUserCert() method may be used inside the constructor (as shown) or in a function that's called separately after contract creation.

Parsing Certificates and Requiring Organization Membership

It may also be possible that you would like to parse a certificate before registering it in STRATO (or without registering it). Another SolidVM function, 'parseCert()`, can be used to achieve this.

Here is a simple example:

pragma solidvm 3.0;
contract identityParser {

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

   constructor() {

       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

Using X.509 and require()

A common reason why you might want to parse a certificate is to determine whether a user is part of an organization. A smart contract can then decide whether or not to allow the user to perform different activities based on group membership.

In this example, we have a Project Manager contract, but only members of Blockapps’s product team can call the function incCounter to add a new project.

pragma solidvm 3.0;
contract ProjectManager {
   int projectCounter = 1;

   function incCounter() {
       require(tx.organization == "Blockapps");
       require( == "product");


When users associated with a registered X.509 certificate post smart contracts, those contracts will automatically be associated with their organization in the database. This allows multiple organizations to create contracts with similar names without creating confusion in the database.

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

Additionally, if BlockApps uses a factory contract to create other contracts, they will also 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

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

                List of relations
 Schema |       Name                                    |   Type    |  Owner   
 public | VendorApps-HelloWorld                         | table | postgres
 public | BlockApps-HelloWorld                              | table | postgres
 public | BlockApps-HelloWorldFactory-HelloWorld            | table | postgres
 public | cold_storage                                      | table | postgres
 public | contract                                          | table | postgres
 public | contract_id_seq                                   | sequence  | postgres
(6 rows)


Cirrus 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/reference the correct contract.


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