KRYSTALM7 opened a new pull request, #130:
URL: https://github.com/apache/fineract-loan-origination/pull/130
## Description
Adds RSA-AES hybrid payload encryption for login credentials to
preventplaintext password exposure in the browser's network tab.
The frontend fetches a fresh RSA-2048 public key from the server before each
login, encrypts the password client-side using AES-256-GCM, wraps the AES key
with RSA-OAEP, and sends only the encrypted envelope over the wire. The server
decrypts using an in-memory private key that is never persisted or logged.
## Changes
### Backend
- Added `CryptoKeyController` exposing `GET /api/v1/auth/public-key`
returning the RSA-2048 public key in SPKI/Base64 format.
- Added `crypto/` package with in-memory RSA-2048 key pair generation on
startup — the private key never leaves the JVM heap and is never logged or
persisted.
- Updated `AuthController` with encrypted endpoint variants: `POST
/api/v1/auth/login/encrypted` and `POST /api/v1/auth/staff/login/encrypted`.
- Updated `FineractAuthenticationProvider` to unwrap the AES key using
RSA-OAEP and decrypt the AES-GCM payload before credential validation.
- Updated `RateLimitFilter` to recognise the `/encrypted` endpoint variants
under the same rate-limiting rules as the plain endpoints.
- Plain `/login` endpoints remain fully functional for local development.
### Frontend
- Added `PayloadEncryptionService` using the Web Crypto API:
- Fetches the server's RSA public key with a cache-busting timestamp to
always match the backend's current in-memory key pair.
- Generates a fresh AES-256-GCM key per login attempt.
- Encrypts the JSON payload with a random 96-bit IV; prepends IV to
ciphertext.
- Wraps the AES key with RSA-OAEP SHA-256.
- Returns `{ wrappedKey, ciphertext }` as Base64 strings.
- Updated `AuthService` to POST the encrypted envelope to
`/api/v1/auth/login/encrypted`.
- Updated `StaffAuthService` to POST the encrypted envelope to
`/api/v1/auth/staff/login/encrypted`.
- Updated all affected frontend unit tests to mock
`PayloadEncryptionService` and assert against the encrypted endpoints.
## Verification
- Verified plaintext password is no longer visible in the browser network
tab on login.
- Verified `GET /api/v1/auth/public-key` returns a valid RSA-2048 public key.
- Verified encrypted login endpoints correctly decrypt and authenticate.
- Verified plain `/login` endpoints remain functional.
- Verified all existing frontend unit tests pass with mocked encryption.
- Verified no credentials appear in logs or response bodies.
## Security Context
This complements the existing server-side hardening already in place:
- Argon2id password hashing (BCrypt-compatible fallback)
- `Cache-Control: no-store` on login responses
- CSP and security headers (nosniff, Referrer-Policy, Permissions-Policy)
- HSTS configured for HTTPS deployments
- Rate limiting on all auth endpoints
- Credential-related fields removed from response bodies and logs
## Notes
The protocol uses RSA-OAEP + AES-256-GCM (hybrid encryption) rather than
RSA-OAEP alone because RSA-OAEP has a maximum plaintext size limited by the key
length. AES-GCM has no such limit and provides authenticated encryption,
ensuring both confidentiality and integrity of the payload.
The RSA key pair is regenerated each time the server starts. A client
holding a stale public key will receive a decryption failure and must retry,
the cache-busting timestamp on `GET /api/v1/auth/public-key` prevents this in
practice.
Closes #101
## JIRA
[FINERACT-2442](https://issues.apache.org/jira/browse/FINERACT-2442)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]