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: 8 min readAuthor: DeepTechHub
#security#appsec#cryptography#certificates#rsa
Practical Application Security for Developers - Part 2: Public Key Cryptography and Certificates

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.

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

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.


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.

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

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
 
# 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

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

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server_key.pem
openssl req -new -key server_key.pem -out server.csr -subj "/CN=localhost"
openssl x509 -req -in server.csr -CA ca/ca_cert.pem -CAkey ca/ca_key.pem \
  -CAcreateserial -out server_cert.pem -days 365 -sha256

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.

Continue Learning

Explore more guides and resources to deepen your knowledge.