A secure, role-based Public Key Infrastructure for issuing, storing, distributing and revoking X.509 certificates. It lets organisations run their own certificate authorities - creating Root, Intermediate and End-Entity certificates across arbitrarily deep chains - while keeping every private key encrypted at rest.
Built as the project for the Information Security course (Informaciona bezbednost). It implements a full certificate lifecycle, envelope-encrypted private-key storage with a rotatable master key, CRL-based revocation, multi-factor authentication, and end-to-end HTTPS using a TLS certificate issued by the PKI itself.
| Capability | How it's implemented |
|---|---|
| Certificate issuance | Issue Root (self-signed), Intermediate and End-Entity certificates over arbitrarily deep chains. The issuer's validity period, signature and revocation status are checked before signing (BouncyCastle). |
| Role-based access | Three roles - Administrator, CA user and Regular user - each scoped to the certificates and chains they're allowed to see, issue and download. |
| Secure key storage | Private keys are encrypted with a per-organization key, which is itself wrapped by a system master key (envelope encryption, AES-256-GCM). |
| Master-key rotation | A scheduled job periodically generates a fresh master key and re-encrypts all dependent data in batches, without interrupting normal use. |
| CSR & autogenerate | End-entity users either upload an externally generated .pem CSR or use autogenerate, where the server creates the key pair, returns it once in a keystore, and never stores the private key. |
| Revocation (CRL) | Certificates are revoked with an X.509 reason; revocation cascades down the chain. CRLs are regenerated on a schedule and served via the CRL Distribution Point extension. |
| Download | Certificate + private key are packaged into a PKCS#12 keystore; certificate-only downloads are offered as .pem / .cer. |
| Authentication | Email/password login with short-lived JWT access tokens and long-lived refresh tokens, plus optional TOTP two-factor authentication. |
| Account recovery | Registration and "forgot password" prove key ownership via a challenge string that is RSA-encrypted to the user's public key and decrypted client-side in the browser (WebCrypto). |
| Package | Responsibility |
|---|---|
| controller | REST endpoints - Auth, Certificate, Crl, TwoFactor, Registration, PasswordReset, Me, and role controllers (Admin, CA, Regular). |
| service.certificate | Certificate generation, issuer validation, keystore export, CRL generation and the scheduled CRL refresh. |
| service.crypto | Master-key rotation - re-encrypts organization keys when the master key changes. |
| service.registration | Registration, key-ownership challenge and password reset. |
| service.user | Per-role business logic (Admin, CA, Regular). |
| crypto | Reusable primitives - AesGcmCryptoService, CertificatePrivateKeyCipher, OrganizationKeyService, MasterKeyProvider, master-key bootstrap & rotation scheduler, TotpSecretCipher. |
| security | JWT authentication filter, UserDetails service and the authenticated-principal model. |
| domain | JPA entities, repositories, enums and embeddables (Certificate, Organization, User, MasterKey, PublishedCrl, RefreshToken, ValidationToken, …). |
Standalone Angular app with lazy-loaded, role-guarded routes. core/ holds typed services and guards (auth, certificate, crypto, admin, ca, regular, registration, password-reset); pages/ holds the screens (login, register, activate, forgot/reset password, profile, and per-role certificate views).
Backend
- Java 21 / Spring Boot 4 - REST API, dependency injection, scheduling.
- Spring Security - JWT authentication, method/route authorization by role.
- Spring Data JPA / Hibernate over PostgreSQL 16.
- BouncyCastle (
bcpkix/bcprov) - X.509 certificate, CSR and CRL generation. - jjwt - access/refresh token signing and validation.
- dev.samstevens.totp - TOTP two-factor authentication.
- SendGrid - transactional email (activation & recovery links).
Frontend
- Angular 21 - standalone components, lazy routes, route guards and an auth interceptor.
- WebCrypto API - client-side RSA decryption for the key-ownership challenge (private keys never leave the browser).
- angularx-qrcode - TOTP enrolment QR codes.
Security building blocks
- AES-256-GCM authenticated encryption for private keys and TOTP secrets.
- Envelope encryption - per-organization keys wrapped by a rotatable system master key.
- X.509 / CRL revocation with cascading reason codes and a CRL Distribution Point.
- HTTPS everywhere, with the TLS certificate issued by this PKI (see docs/HTTPS.md).
Prerequisites: JDK 21, Node.js + Angular CLI, a running PostgreSQL 16 with a database named ib, and a SendGrid API key for email.
# 1. Backend - from backend/ib
# Provide the DB password, mail key, and (for HTTPS) the keystore password as env vars.
export DB_PASSWORD="..." SENDGRID_API_KEY="..." KEYSTORE_PASSWORD="..."
./mvnw spring-boot:run
# 2. Frontend - from frontend/ib
npm install
npm startThe app is served at https://localhost:4200; the backend listens on https://localhost:8443 and the frontend reaches it through the dev-server proxy.
First run / HTTPS bootstrap: the backend needs a TLS keystore to start, but that certificate is issued by this very PKI. Run the stack once on plain HTTP, issue a
localhostserver certificate from the admin UI (withdns:localhost, ip:127.0.0.1SANs), drop the keystore in place, then restart over HTTPS. The full step-by-step - including trusting the Root CA in your OS - is in docs/HTTPS.md.
- Log in as admin and create an organization with a CA user, or register as a regular user (you'll need an RSA key pair to prove ownership during activation).
- As admin or a CA user, issue a Root → Intermediate → End-Entity chain under Certificates → Issue.
- As a regular user, upload a CSR or use autogenerate, download the PKCS#12 keystore, and later revoke the certificate with an X.509 reason.