<PS/>
Article

How We Made Academic Certificates Verifiable Without a Blockchain

CPRBD at the University of Dhaka needed a way for employers to verify that a professional certificate was legitimate. The solution turned out to be simpler than you'd think: HMAC hashes, a verification endpoint, and QR codes.
PSParvej Shah
December 18, 2025· Last updated: August 26, 20264 min read
Cryptographic Credential Verification Cover

When the Center for Policy Research on Business and Development (CPRBD) at the University of Dhaka approached us about building their institutional web portal, one requirement stood out immediately: certificate verification.

CPRBD runs executive education programs and professional certification cohorts for mid-career government officials and business professionals. Participants receive physical certificates signed by faculty from the University of Dhaka's Department of International Business. These certificates are used as credentials when applying for government positions, international postings, and senior roles.

The problem: nothing on the physical certificate was verifiable. An employer looking at a CPRBD Executive Certification had no way to confirm it was authentic. The certificate could have been genuine, fraudulently obtained, or simply printed on a good color printer.

Why HMAC Hashes Are Enough

The core requirement for certificate verification is this: given a certificate ID printed on the physical certificate, an employer should be able to query a public endpoint and receive confirmation that a specific person completed a specific program.

Blockchain solutions add tamper-evidence and decentralization. Both are useful properties in some contexts. For institutional certificate verification — where CPRBD controls the issuing authority and the verification endpoint — neither is necessary. The trust anchor is CPRBD's public-facing website, not a distributed ledger.

A simple HMAC hash is tamper-evident: it is computationally infeasible to produce a valid verification code without knowing the secret key, which only CPRBD's server holds.

function generateCertificateVerificationCode(
  certificateNumber: string,
  recipientName: string,
  programName: string,
  secretKey: string
): string {
  const payload = [certificateNumber, recipientName, programName]
    .join(":")
    .toLowerCase()
    .trim();

  return crypto
    .createHmac("sha256", secretKey)
    .update(payload)
    .digest("hex")
    .substring(0, 16)
    .toUpperCase();
}

Each certificate is issued with a verification code generated from its unique attributes: the certificate number, the recipient's name exactly as it appears on the certificate, and the program name. The code is printed on the certificate and embedded in a QR code that links directly to the verification page.

The Verification Endpoint

When an employer scans the QR code or enters the verification code manually, the endpoint performs the inverse: retrieve the certificate record by certificate number, recompute the verification code using the stored attributes, and compare to the submitted code.

async function verifyCertificate(
  certificateNumber: string,
  submittedCode: string
): Promise<VerificationResult> {
  const certificate = await db.certificate.findUnique({
    where: { certificateNumber },
    include: { recipient: true, program: true },
  });

  if (!certificate) {
    return { valid: false, reason: "Certificate number not found" };
  }

  const expectedCode = generateCertificateVerificationCode(
    certificate.certificateNumber,
    certificate.recipient.name,
    certificate.program.name,
    process.env.CERTIFICATE_SECRET_KEY!
  );

  const valid = crypto.timingSafeEqual(
    Buffer.from(expectedCode, "utf-8"),
    Buffer.from(submittedCode.toUpperCase().trim(), "utf-8")
  );

  if (!valid) {
    return { valid: false, reason: "Verification code does not match" };
  }

  return {
    valid: true,
    recipient: certificate.recipient.name,
    program: certificate.program.name,
    completionDate: certificate.completedAt,
    grade: certificate.grade,
  };
}

The endpoint responds in under 100 milliseconds and requires no authentication from the verifying party. It's intentionally public — anyone with a certificate number and verification code can confirm its authenticity. Only CPRBD can issue valid codes.

The Administrative Side

The less visible but equally important part of the platform is the administrative interface for cohort and certificate management. Program coordinators need to define cohorts, enroll participants, track attendance and completion, and issue certificates when participants meet the requirements.

We built the admin interface with a focus on bulk operations — importing participant lists from spreadsheets, issuing certificates to an entire cohort in a single action, and generating batch QR code PDFs for physical printing. The alternative — managing 60 participants in a form-based interface one at a time — was the previous workflow, and it took days.

The research publication repository was a separate section: structured metadata with attached PDF assets, full-text search, and a clean reading interface. University of Dhaka's Department of International Business has a significant body of published research, and the previous approach was a static list in a Word document on the university website. The structured repository made research discoverable and made CPRBD's intellectual output visible to policy practitioners and government stakeholders who otherwise wouldn't find it.

Enjoyed the read?

Have a product idea worth building — let's talk.

Start a project