Developer Hub

Welcome to CertCenter's Developer Hub. You'll find comprehensive guides and documentation to help you start working with CertCenter as quickly as possible, as well as support if you get stuck.

Let's jump right in!

Get Started Full API Reference

Comodo Domain Control Validation

1. Preface

If you are using CertCenter's Certificate Manager, you might not notice any unusual behavior with the DCV mechanism at Comodo. But there are some tweaks concerning the validation tokens you're might be interested in - at least as a developer.

The purpose of DCV is to undoubtedly determine that the applicant has control over the domain names (or at least the fully qualified domain names) used in the certificate order. To ascertain this, applicants shall choose between the validation methods "EMAIL", "FILE" or "DNS".

Applicants who chose FILE/DNS based domain control validation need to create a text file or DNS entries for all their FQDNs[] (read more).

At Comodo, the name of the file, as well as its content, consists of two different tokens:

  • Name: /.well-known/pki-validation/<CSRs-MD5-Hash>.txt
  • Content: Line #1: <CSRs-SHA256-Hash>, Line #2:, Line #3: Unique Value

2. Calculating The Validation Hashes For DNS or FILE based DCV

On CertCenter's Certificate Manager, we're going to provide you with all necessary information in the last step of the order process. But as a developer and integration partner, you might be curious about how to fetch or calculate that hashes by yourself and to create the required files or DNS entries even before submitting the order.

Option #1: Get The Hashes From CertCenter's API

It's quite simple to get the MD5 hash, the SHA256 hash and the UniqueValue you need for Comodo DNS/FILE based DCV. Just call the POST /ValidateCSR method with your CSR:

import CertCenter
api = CertCenter.CertAPI()
res = api.ValidateCSR(req={
	"CSR": "#CSR#"
print repr(res)
require_once '';
use CertCenter\RESTful as ccAPI;
$api = new ccAPI();
print_r( $api->ValidateCSR(Array("CSR"=>"#CSR#")) );
package main

import (
	_ "io/ioutil"
	_ "time"
	certcenter ""

// Set your valid OAuth2 Bearer
// (see

func init() {
	certcenter.Bearer = ""

func main() {
	// Validate a CSR
	csr, _ := ioutil.ReadFile("csr")
	res, _ := certcenter.ValidateCSR(&certcenter.ValidateCSRRequest{
		CSR: string(csr),
curl -X POST '' \
  -H 'Content-Type: application/json' \
  -d '{ "CSR": "#CSR#" }'
  "ParsedCSR": {
    // { .. A lot of information about your CSR }
    "HashMD5": "BFA79077625AE5F73E673A83AF6CA3E8",
    "HashSHA256": "B442C0286FE2D8DA085DED2F31FED51F0B45FEA3721C2282A41F8BF36DF782B9",
    "UniqueValue": "516C4B86A67AFD1A266F"
  "success": true

Yes, it's that easy! Just grab the values in ParsedCSR.HashMD5, ParsedCSR.HashSHA256 and ParsedCSR.UniqueValue. Those are deterministic values. That means, as long you are using the same CSR, those hashes will be the same. There are no random or time components included in the hash calculation, except ParsedCSR.UniqueValue - that value is calculated by HashSHA256, time and salt (more details later). That's not the case with other CA's, they use pre-shared secrets which are also used as one of the multiple components to calculate the hash, and CertCenter, unfortunately, isn't allowed to share those pre-shared keys.

Option #2: Calculate The Hashes By Yourself

You can easily calculate those hashes by yourself. You just need to use the CSR's binary version (=DER) instead of the PEM (base64) encoded version to create the checksum with the commands md5sum/sha256 sum.

$ openssl req -outform der -in your.csr | md5sum -b | awk '{ print $1 }'
$ openssl req -outform der -in your.csr | sha256sum -b | awk '{ print $1 }'

The UniqueValue will be built once you call the method /ValidateCSR or /Order and is valid for at least 300 seconds. It is calculated by a md5 hexdigest from the number of minutes until midnight + HashSHA256. It's then shortened to 20 characters and converted to uppercase.

from datetime import datetime
n =
time_component = ((24-n.hour-1)*60)+(60-n.minute-1)
s = "%d%s" % (time_component, SHA256Hash)
UniqueValue =
UniqueValue = UniqueValue[:20].upper()

3. A Different Behavior On DNS Based Validation

Please note that there is a slightly different behavior on DNS based validation. It took the same MD5 and SHA256 hashes, but a little modified. Due to a limitation to the max length for hostnames, you have to split the SHA256 hash into two parts. 60 IN CNAME 60 IN CNAME
_#HashMD5#.#FQDN#. 60 IN CNAME

Do I Really Need The Hashes Before Submitting An Order?

If you don't care about the creation of the required files or DNS entries before submitting the order, you can just grab the hashes from the POST /Order response. No worries; it also works very well! It's a fundamental architectural decision to create the files or the DNS entries before submitting the order. A web host will possibly make use of the preset feature, while the effort for a normal user will be too high.