Certificates provide proof of ownership for a specific address. In combination with wallets, they offer a standardized method for signing agreements or creating a sign-in experience with VeChain.
Common use cases include user identification through sign-in and request verification between services.
The structure of a certificate is clearly defined and includes a purpose, payloads, domain, and signer.
interface Certificate {
/**
* The purpose field indicates the intended use or context of the certificate.
* For example, it could be used for identification, verification, or attestation.
*/
purpose: string;
/**
* The payload field holds the actual content of the certificate.
* This content can be of various types, such as text, images, or other data.
*/
payload: {
type: string;
content: string;
};
/**
* The domain field represents the specific context or domain for which the certificate is valid.
* It helps ensure that the certificate is only applicable within the intended context.
*/
domain: string;
/**
* The timestamp field records the time at which the certificate was created or issued.
* This provides a temporal reference for the certificate's validity.
*/
timestamp: number;
/**
* The signer field indicates the address of the entity that signs the certificate.
* It is the public key address of the entity that issues the certificate.
*/
signer: string;
/**
* The signature field contains the cryptographic signature generated by the issuer's private key.
* This signature ensures the integrity and authenticity of the certificate's content.
*/
signature?: string;
}
To sign a certificate yourself, you will need to:
- Obtain the signer's address.
- Encode the certificate object into a JSON string. Avoid using
JSON.stringify
directly, as the key order may vary, potentially causing verification failures across different platforms, usecertificate.encode()
instead. - Sign the encoded string.
If you possess only the private key, you can derive the public address from its corresponding public key:
// calculate signer from private key
const publicKey = secp256k1.derivePublicKey(privateKey);
cert.signer = addressUtils.fromPublicKey(publicKey);
Signing example:
// encode & sign
const jsonStr = certificate.encode(cert);
const rawSignature = secp256k1.sign(blake2b256(jsonStr), privateKey);
const signature = `0x${rawSignature.toString('hex')}`;
Adding the signature
attribute to the certificate turns it into a signed certificate, available for verification on other platforms.
{% hint style="info" %} An API verification can consist of a client sending JSON.stringified and base64 encoded version in the headers. The API can then decode and verify the certificate. {% endhint %}
Verification is done using certificate.verify(cert)
which will throw in case of any error. Catching errors is therefor important:
try {
certificate.verify(signedCert);
console.log('verification successful')
}
catch (err) {
console.log('verification failed', err.message)
}
{% embed url="https://stackblitz.com/github/vechain-energy/example-snippets/tree/v1.0.0/sdk/certificate?ctl=1&embed=1&file=index.mjs&hideExplorer=1&hideNavigation=1&view=editor" %}