Skip to main content

The QCAT Standard: Implementing Public Key Management System for QR Code Signing



In this guide, we’ll explore how to implement a Public Key Management System for signing QR codes, focusing on the QCAT Signature Algorithms Version 1 and 2.

Key Management Overview

The QR Code signature system relies on cryptographic keys and certificates to ensure the authenticity of the QR codes generated by issuers. The key players in this system are the Certificate Authority (CA) and the QR Code Ticket Issuers.

1. Certificate Authority (CA):

  • The CA signs the issuer’s public keys using its private key, creating a public key certificate. This certificate ensures the QR codes can be verified at validation terminals.
  • CA uses a 2048-bit RSA key to sign the issuer’s keys, ensuring a high level of security.
2. Ticket Issuer:
  • Each issuer generates their own public/private key pairs. The private key is used to sign QR codes, while the public key is included in the certificate signed by the CA.
  • Validation terminals use the issuer’s public key to verify the signature on a QR code.
3. Validation Process:
  • The CA’s self-signed certificate is installed in all terminals.
  • When a QR code is presented, the terminal retrieves the corresponding issuer certificate and verifies it using the CA’s public key.
  • The terminal then uses the issuer’s public key to verify the QR code’s signature.

Creating a Certificate Authority Private Key and Public Key Certificate

To generate the CA's key pair and public certificate, we use OpenSSL. Here’s how:

1. Create the OpenSSL configuration file, openssl.cnf:

[ req ]
default_bits = 2048
default_md = sha256

2. Create the CA’s private key and self-signed certificate:

$ openssl req -new -x509 -keyout afpi-ca-key.txt -out afpi-ca-certificate.pem.txt -days 3650 -config openssl.cnf

This command generates a 2048-bit RSA private key and a self-signed public certificate valid for 10 years.

3. The distinguished name (DN) for the CA certificate includes details like the country, organization, and a common name for identification. Here’s an example:

Country Name (2 letter code) []:PH
State or Province Name (full name) []:NCR
Locality Name (eg, city) []:MAKATI
Organization Name (eg, company) []:AF PAYMENTS INC
Organizational Unit Name (eg, section) []:AFPI-0000
Common Name (eg, fully qualified host name) []:QCAT-001
Email Address []:john.doe@afpayments.com

Issuer Key Generation and Certification

Ticket issuers also generate their own private/public key pairs. The public key is signed by the CA to create a certificate that is installed in validation terminals.

1. Generate the issuer’s private key and keystore using Java's keytool:

$ keytool -genkey -alias bank-qr-issuer-key -keyalg RSA -keysize 1024 -validity 1825 -storetype PKCS12 -keystore bank-qr-issuer-keystore.p12

This generates a 1024-bit RSA key pair, stored in a PKCS12 keystore file.

2. The distinguished name (DN) for the key includes details like the country, organization, and a common name for identification. Here’s an example:

Country Name (2 letter code) []:PH
State or Province Name (full name) []:NCR
Locality Name (eg, city) []:MAKATI
Organization Name (eg, company) []:BANK NAME
Organizational Unit Name (eg, section) []:BANK-00123
Common Name (eg, fully qualified host name) []:BANK BACKEND

3. Request signing of issuer's public key to the CA:

$ keytool -certreq -keystore bank-qr-issuer-keystore.p12 -alias bank-qr-issuer-key -keyalg rsa -file bank-qr-issuer.csr

4. The issuer’s certificate is then signed by the CA, allowing validation terminals to trust QR codes signed by the issuer’s private key.

$ openssl x509 -req -CA afpi-ca-certificate.pem.txt -CAkey afpi-ca-key.txt -in bank-qr-issuer.csr -out bank-bank-qr-issuer.cer -days 1825 -CAcreateserial

5. Lastly, the root public key certificate of the CA is then loaded into the issuer's keystore.

$ keytool -import -keystore bank-qr-issuer-keystore.p12 -file afpi-ca-certificate.pem.txt -alias afpi-ca-root

Now, when you try to load the public key certificate of the issuer, the keytool should no longer ask whether the certificate is trusted. This is due to the loading of the root public key certificate of the CA earlier.

$ keytool -import -keystore bank-qr-issuer-keystore.p12 -file afpi-bank-qr-issuer.cer -alias bank-issuer

Summary of the Key and Certificate Created

As a result of all the commands executed, we now have the following:

  • bank-qr-issuer-keystore.p12 - the file contains the Issuer’s keystore, which includes the following keys and certificates: the bank-qr-issuer-key, a private key used for generating QR code signatures; the afpi-ca-root, the self-signed root certificate of the CA; and the bank-issuer, a CA-signed issuer certificate used to validate QR code signatures.
  • afpi-ca-certificate.pem.txt - the CA’s self-signed public key certificate is used to create new keystores for validation terminals.
  • afpi-ca-key.txt - the password-protected private key of the CA is used to generate new public key certificates for additional QCAT Ticket Issuer certificates.
  • afpi-bank-qr-issuer.cer - the QCAT Ticket Issuer’s CA-signed public key certificate can be imported into keystores to validate QR code signatures generated by the issuer.
  • bank-qr-issuer.csr - can be deleted

Secure QR Code Signing

Once the issuer's keys are generated and certificates distributed to terminals, the issuer can use their private key to sign QR code data. The signature is then attached to the QR code, ensuring that only valid, authorized issuers can create authentic QR codes.

Summary of Steps:

1. Generate CA private/public key pair.

2. Issue and install self-signed CA certificate on validation terminals.

3. Generate issuer’s private/public key pairs.

4. Sign the issuer’s public key with the CA’s private key.

5. Distribute issuer certificates to terminals.

6. Sign QR codes with the issuer’s private key for validation.

Signing QR Codes with the Issuer’s Private Key

To ensure the authenticity and integrity of the QR codes issued, the following steps outline how to sign QR codes using the issuer's private key:

Step 1: Prepare the QR Code Data

1. Define the Data to be Signed: Determine what data will be encoded in the QR code, such as transaction details, issuer information, and any other relevant data. 

2. Convert Data to Byte Array: Convert the data into a format suitable for signing (e.g., UTF-8 encoded byte array).

val qrData = "Transaction details here"
val dataToSign = qrData.toByteArray(Charsets.UTF_8)

Step 2: Load the Issuer’s Private Key

1. Load the Keystore: Load the keystore that contains the issuer's private key.

val keystore = KeyStore.getInstance("PKCS12")
FileInputStream("bank-qr-issuer-keystore.p12").use { fis ->
    keystore.load(fis, "keystore-password".toCharArray())
}

2. Retrieve the Private Key: Get the private key from the keystore using its alias.

val privateKey = keystore.getKey("bank-qr-issuer-key", "key-password".toCharArray()) as PrivateKey

Step 3: Sign the Data

1. Initialize the Signature: Create a Signature object and initialize it with the issuer's private key.

val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(privateKey)

2. Update and Sign the Data: Update the signature with the data and sign it.

signature.update(dataToSign)
val signedData: ByteArray = signature.sign()

Conclusion

The Public Key Management System is essential for ensuring the authenticity and integrity of QR codes used in various applications. By following the steps outlined above, issuers can securely sign QR codes, enabling validation and enhancing trust in transactions.

If you’d like to learn more or get involved in the QCAT ecosystem, feel free to check out my guides on QCAT specification below or check out AF Payments Inc.'s GitHub for the latest updates and contribution guidelines.

Don't forget to subscribe to my blog so you never miss out on my latest guides and content!

DISCLAIMER

The information provided in this article is for informational purposes only and does not constitute professional advice. While efforts have been made to ensure accuracy, AF Payments Inc. reserves the right to update or modify the QCAT standard and related materials at any time. Use of the QCAT standard is subject to the terms of its license agreement, and any implementation must adhere to AFPI's guidelines and licensing requirements. For the latest details and official documentation, please refer to AF Payments Inc.'s authorized channels.

Comments

Popular posts from this blog

Understanding Number Systems: Decimal, Binary, and Hexadecimal

In everyday life, we use numbers all the time, whether for counting, telling time, or handling money. The number system we’re most familiar with is the   decimal system , but computers use other systems, such as   binary   and   hexadecimal . Let’s break down these number systems to understand how they work. What is a Number System? A number system is a way of representing numbers using a set of symbols and rules. The most common number systems are: Decimal (Base 10) Binary (Base 2) Hexadecimal (Base 16) Each system has a different “base” that tells us how many unique digits (symbols) are used to represent numbers. Decimal Number System (Base 10) This is the system we use daily. It has  10 digits , ranging from  0 to 9 . Example: The number  529  in decimal means: 5 × 1⁰² + 2 × 1⁰¹ + 9 × 1⁰⁰ =  500 + 20 + 9 = 529 Each position represents a power of 10, starting from the rightmost digit. Why Base 10? Decimal is base 10 because it has 10 digits...

How to Monetize Your API as an Individual Developer While Hosting on Your Own Server?

In the API economy, cloud services like AWS, Google Cloud, and Azure offer many conveniences, such as scaling and infrastructure management. However, some developers prefer more control and autonomy, opting to host their APIs on personal servers. Whether for cost efficiency, data privacy, or customization, hosting your own API comes with both advantages and challenges. But, even without cloud platforms, there are effective ways to monetize your API. This guide will explore how individual developers can successfully monetize their APIs while hosting them on their own servers. Why Host Your API on Your Own Server? Hosting your own API gives you full control over the infrastructure and potentially lower long-term costs. Here’s why some developers choose this approach: Cost Control : Instead of paying ongoing cloud fees, you may opt for a one-time or lower-cost hosting solution that fits your budget and resource needs. Data Ownership : You have full control over data, which is critical if ...

The Weight of Responsibility: A Developer’s Journey to Balance Passion and Reality

For the past several years, Eddie has been on a steady climb in his career as a developer, but recently, he found himself at a crossroads — caught between the weight of his responsibilities and the desire to pursue his true passions. His journey began with a three-month internship as a web developer, which led to nearly four years in an application developer role. After that, he spent almost a year as a systems associate, managing tasks across systems analysis, quality assurance, and business analysis. Eventually, he returned to full-time software development for another two years before transitioning into more complex roles. For over a year, he worked as a multi-role software developer and database administrator before stepping into his current position as a senior software developer, database administrator, and cloud administrator — occasionally handling security tasks as well. Now, with over 8 years of professional experience, he also leads a small team of developers, which has been...

The Hidden Costs of Overdesign and Bad Practices in API Systems

In software development, simplicity and clarity are often sacrificed in favor of overly complex solutions. While it can be tempting to add more features and intricate designs to ensure robustness, overdesign and poor practices can have significant consequences. They frustrate developers, lead to inefficiencies, increase costs, and put unnecessary strain on system resources.  A recent example involving a team that has faced challenges with complexity highlights the pitfalls of such an approach. Overdesign: The Problem of Too Much Complexity Overdesign occurs when systems are built with more complexity than necessary. This might manifest in bloated APIs, convoluted data flows, or excessive checks and processes that don’t add substantial value. The goal is often to anticipate future problems, but this approach typically results in cumbersome systems that are difficult to maintain and scale. In one case, a company found itself paying a hefty price just to host two API services and a po...

Selenium for Beginners: What, Where, When, and Why to Use It in Automated Testing

In today’s software development landscape, automated testing has become essential for delivering robust applications efficiently. Among various automated testing tools,   Selenium   stands out as one of the most widely used and beginner-friendly options. As you embark on your journey into automated testing, it’s crucial to understand the   what, where, when, and why   of using Selenium. In this guide we will run through these essentials and help you decide if Selenium is the right tool for you. What is Selenium? Selenium  is an open-source framework used primarily for automating web browsers. It enables developers and testers to write scripts that interact with websites, simulating actions like clicking buttons, filling out forms, and navigating pages, which allows for comprehensive automated testing. Selenium supports multiple programming languages, including Python, Java, C#, and JavaScript, making it flexible for teams with different coding preferences. Key C...