security

Practical Application Security for Developers - Part 2: Public Key Cryptography and Certificates

Learn RSA, ECC, digital signatures, X.509 certificates, and practical certificate handling for modern applications.

Reading Time: 10 min readAuthor: DeepTechHub
#security#appsec#cryptography#certificates#rsa
Practical Application Security for Developers - Part 2: Public Key Cryptography and Certificates

In Part 1 we covered symmetric primitives like hashing, HMAC, and AES. Those work well when both parties already share a secret. The harder question is this: how do two systems that have never talked before establish trust safely over an untrusted network?

That is the key distribution problem, and public key cryptography is the reason HTTPS, code signing, and certificate-based trust can work at internet scale.

This article covers:

  1. How asymmetric key pairs work
  2. RSA versus elliptic-curve cryptography
  3. Digital signatures and when they matter more than encryption
  4. X.509 certificates and trust chains
  5. A practical local CA setup for development

The Core Idea: Asymmetric Key Pairs

Symmetric encryption uses one secret for both encryption and decryption. Asymmetric cryptography splits that responsibility across a key pair known as public and private keys.

A private and public key are a mathematically linked pair where each one only works with the other. For example, data encrypted with the public key can only be decrypted with the private key, and data signed with the private key can be verified with the public key.

data-integrity-attack
Fig: Key-pairs exchange

In the private-public key pair, the private key is used for most critical part of the operation.

KeyWho holds itWhat it does
Public keyAnyoneEncrypts data or verifies signatures
Private keyOnly the ownerDecrypts data or creates signatures
   CLIENT SIDE                          SERVER SIDE

   Public Key                           Private Key
   (shareable)                          (secret)

        │                                   │
        ▼                                   ▼
   Encrypt Data                      Decrypt Data
   Verify Signature                  Create Signature

The important consequence is that you can publish the public key without exposing the private key. That breaks the circular dependency that symmetric systems have: you no longer need a secure channel before you can create a secure channel.


There are two most commonly used public key cryptography systems:

  • RSA
  • Elliptic Curves

RSA

RSA is based on the difficulty of factoring very large integers. It remains widely supported and still appears in many production systems, although new systems increasingly prefer elliptic-curve algorithms.

Practical RSA Choices

ParameterRecommendationWhy
Key size2048-bit minimum, 4096-bit for long-lived keys1024-bit is no longer acceptable
Encryption paddingOAEPSafer than PKCS#1 v1.5
Signature paddingPSS when possibleStronger modern RSA signature mode

Why RSA Is Usually Used in Hybrid Form

RSA is much slower than AES for bulk data. In practice, systems usually do this instead:

  1. Generate a random AES key
  2. Encrypt the real payload with AES-GCM
  3. Encrypt the AES key with the recipient's RSA public key
  4. Send both together

That is why RSA is often used for key wrapping while AES handles the real data.


Digital Signatures

Encryption answers, "Can only the intended recipient read this?" Digital signatures answer a different question: "Can I prove who produced this data and whether it was modified later?"

Signing Flow

  1. Hash the message
  2. Sign that hash with the private key
  3. Send the message and signature together

Verification Flow

  1. Hash the received message
  2. Verify the signature with the public key
  3. Reject the data if verification fails

This matters for:

  • JWTs signed with asymmetric keys
  • Code-signing pipelines
  • Certificates in TLS
  • Signed documents and artifacts

HMAC versus Digital Signatures

PropertyHMACDigital signature
Key modelShared secretPublic/private key pair
Non-repudiationNoYes
PerformanceVery fastSlower
Best fitInternal trust boundaryThird-party verification and PKI

RSA Signing in Java

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
 
public class RsaSignatureUtil {
 
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(4096, new SecureRandom());
        return generator.generateKeyPair();
    }
 
    public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }
 
    public static boolean verify(PublicKey publicKey, byte[] data, byte[] signed)
            throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(signed);
    }
}

Elliptic Curve Cryptography

RSA is familiar, but ECC gives equivalent security with much smaller keys.

Security levelRSA key sizeECC key size
128-bit3072-bit256-bit
192-bit7680-bit384-bit
256-bit15360-bit521-bit

Why Teams Prefer ECC in New Systems

  • Smaller certificates and keys
  • Faster handshakes
  • Lower CPU cost at scale
  • Better fit for mobile and high-volume APIs

Curves You Will Actually See

CurveTypical use
P-256TLS certificates, ES256 JWTs
P-384Higher-security environments
Ed25519SSH keys, modern signing
X25519Key exchange, especially TLS 1.3

ECC Signing in Java

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
 
public class EcdsaSignatureUtil {
 
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
        generator.initialize(256);
        return generator.generateKeyPair();
    }
 
    public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
        Signature signature = Signature.getInstance("SHA256withECDSA");
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }
 
    public static boolean verify(PublicKey publicKey, byte[] data, byte[] signed)
            throws Exception {
        Signature signature = Signature.getInstance("SHA256withECDSA");
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(signed);
    }
}

X.509 Certificates

Public keys alone are not enough. A client still needs to know whether the key it received really belongs to the identity it claims to represent. X.509 certificates solve that by binding a public key to an identity and having a trusted certificate authority sign that binding.

Certificate lifecycle

The Core Fields

FieldPurpose
SubjectWho the certificate belongs to
IssuerWhich CA signed it
ValidityWhen the certificate is valid
Public keyThe certified key material
SANDNS names or IPs the cert covers
SignatureProof from the issuer
Client Trust Store
        │
        ▼
   [ Root CA ]
        │
        ▼
[ Intermediate CA ]
        │
        ▼
[ Server Certificate ]

Verification succeeds if the chain terminates at a trusted root.

Helpful OpenSSL Commands

# Download a remote certificate
echo | openssl s_client -servername github.com -connect github.com:443 2>/dev/null \
    | openssl x509 > github-cert.pem
 
# Inspect certificate contents
openssl x509 -in github-cert.pem -noout -text
 
# View just subject and issuer
openssl x509 -in github-cert.pem -noout -subject -issuer
 
# Check dates
openssl x509 -in github-cert.pem -noout -dates
 
# Extract the public key
openssl x509 -in github-cert.pem -noout -pubkey

Certificate Format Cheat Sheet

FormatExtensionTypical use
PEM.pem, .crtHuman-readable certificate and key files
DER.der, .cerRaw binary certificate
PKCS#12.p12, .pfxCertificate plus private key bundle
JKS.jksLegacy Java keystore format

Verifying Certificate Authenticity

Trust usually flows through a chain:

  1. Your server certificate is signed by an intermediate CA
  2. That intermediate is signed by a root CA
  3. Your OS, browser, or JVM already trusts that root CA
openssl verify github-cert.pem

If verification fails because the issuer is missing, provide the intermediate explicitly:

openssl verify -CAfile intermediate-ca.pem github-cert.pem

How CAs Verify You Own a Domain

Before a public CA issues a certificate, it needs proof you control the domain. There are three standard methods:

  • Email — the CA sends a verification link to an administrative address on the domain (e.g. admin@example.com). Simple but manual.
  • HTTP-01 — the CA gives you a token file to serve at http://yourdomain/.well-known/auth-challenge/<token>. The CA fetches it to confirm you control the web server. This is what Let's Encrypt's Certbot automates.
  • DNS-01 — the CA gives you a TXT record value to add to your domain's DNS config. The CA queries DNS to verify it. The only method that works for wildcard certificates (*.example.com).

Let's Encrypt is a free, non-profit CA that automates the entire HTTP-01 and DNS-01 flow — worth knowing about before paying for certificates.

What Happens When a Private Key Is Compromised

If a certificate's private key is stolen or accidentally exposed, the certificate owner asks the CA to revoke it. The CA adds it to a Certificate Revocation List (CRL) or responds to OCSP queries so that browsers and clients know to reject it before the certificate's natural expiry date. This is why committing private keys to source control is in the mistakes section — revocation is reactive, not instant.


Build a Local Development CA

A local CA gives you trusted HTTPS in development without training yourself to ignore browser warnings.

1. Create the CA

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out ca/ca_key.pem
openssl req -x509 -new -key ca/ca_key.pem -days 3650 \
  -out ca/ca_cert.pem -subj "/CN=Local Dev CA"

2. Install It in the Trust Store

macOS

sudo security add-trusted-cert -d -r trustRoot \
  -k "/Library/Keychains/System.keychain" ca/ca_cert.pem

Ubuntu or Debian

sudo cp ca/ca_cert.pem /usr/local/share/ca-certificates/local-dev-ca.crt
sudo update-ca-certificates

Windows PowerShell

Import-Certificate -FilePath ca\ca_cert.pem -CertStoreLocation Cert:\LocalMachine\Root

3. Create and Sign a Server Certificate

First, create ca/server-cert.cnf:

[ req ]
prompt             = no
default_md         = sha256
distinguished_name = req_dn
req_extensions     = req_ext
 
[ req_dn ]
CN = localhost
 
[ req_ext ]
subjectAltName     = @alt_names
basicConstraints   = CA:FALSE
keyUsage           = critical, digitalSignature, keyEncipherment
extendedKeyUsage   = serverAuth
 
[ alt_names ]
DNS.1 = localhost
IP.1  = 127.0.0.1
IP.2  = ::1

Then generate the server key, CSR, and signed certificate:

# Generate server key
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server_key.pem
 
# Generate CSR using config
openssl req -new -key server_key.pem -out server.csr -config ca/server-cert.cnf
 
# Sign with SAN extensions included
openssl x509 -req \
  -in server.csr \
  -CA ca/ca_cert.pem \
  -CAkey ca/ca_key.pem \
  -CAcreateserial \
  -out server_cert.pem \
  -days 365 \
  -sha256 \
  -extensions req_ext \
  -extfile ca/server-cert.cnf

4. Verify It

openssl verify -CAfile ca/ca_cert.pem server_cert.pem

Common Mistakes to Avoid

  1. Using RSA 1024-bit keys
  2. Using old padding modes when OAEP or PSS are available
  3. Forgetting SANs and relying on CN alone
  4. Committing private keys to source control
  5. Disabling certificate verification in production
  6. Treating expiry monitoring as optional

Summary

TopicTakeaway
Asymmetric keysSolve the key-distribution problem
RSAStill useful, but heavier
ECCBetter default for many new systems
Digital signaturesProve origin and integrity
X.509Binds keys to identity through a trust chain
Local CAMakes development TLS much safer and less annoying

What's Next

In Part 3, we will put these keys and certificates to work in the real world: TLS 1.3, mTLS, ingress termination, and production hardening for Spring Boot and Kubernetes.

Did you find this article useful?