Secure Spring Boot APIs with Hybrid AES‑RSA Encryption, Transparent Request Wrappers, and Response‑Encryption AOP
This article walks through a complete solution for protecting data in Spring Boot applications, covering symmetric, asymmetric, and hybrid encryption schemes, utility classes for AES and RSA, a custom HttpServletRequestWrapper to enable multiple reads of the request body, a filter that transparently decrypts incoming parameters, and an AOP‑based response‑encryption mechanism triggered by a custom annotation.
Ensuring secure data transmission between front‑end and back‑end is critical; while HTTPS provides transport‑level protection, many systems add an extra application‑layer encryption step. The article explains three encryption approaches—symmetric (AES), asymmetric (RSA), and hybrid (AES encrypted with RSA‑encrypted key)—and shows how to implement them in a Spring Boot project.
1. Encryption Utilities
AESUtil encapsulates AES operations. It declares constants such as AES_ALGORITHM = "AES", provides methods to generate random keys ( getAESKey(int length) and generateRandomAesKeyWithBase64()), and offers multiple encrypt/decrypt APIs that accept raw bytes, Base64 strings, or Hex strings. Helper methods toHexString(byte[]) and toBytes(String) convert between byte arrays and hexadecimal representations, while encryptBASE64 / decryptBASE64 handle Base64 encoding.
RSAUtil supplies RSA key‑pair generation ( generateRandomToBase64Key()), block‑wise encryption/decryption ( encryptByPublicKey, decryptByPrivateKey), and utilities for Base64/Hex conversion. It also includes methods for digital signatures ( generateSign, verify).
2. Transparent Request Reading
The RequestWrapper class extends HttpServletRequestWrapper. In its constructor it copies the original request body into a byte[] body field, handling both regular and MultipartRequest cases. Overridden getReader() and getInputStream() return streams backed by this cached byte array, allowing the request body to be read multiple times. Additional methods setBody(byte[]) and setParameterMap(Map<String,Object>) let downstream filters or interceptors replace the body and inject extra parameters, while getParameter, getParameterMap, and related methods are overridden to expose the injected map.
3. Decryption Filter
The DecryptReplaceStreamFilter implements Filter. It first checks a configuration flag ( API.Security.enable) and the request header aksEncrypt to decide whether decryption is required. For POST requests it reads the JSON body, extracts the encrypted content (Base64) and the RSA‑encrypted aesKey, decrypts the AES key with the server’s private RSA key, then decrypts the payload with AESUtil.decryptFromBase64. The decrypted data replaces the request body via requestWrapper.setBody, and the AES key is stored in the wrapper’s parameter map for later use. For GET requests it obtains the encrypted query parameter encryptData and the RSA‑encrypted AES key from the header, performs the same RSA‑then‑AES decryption, converts the Hex string to bytes, and populates the parameter map.
The filter is registered with the highest order ( registration.setOrder(1)) to ensure decryption occurs before any business logic.
4. Response Encryption via AOP
A custom annotation @ResponseEncrypt marks controller methods whose responses should be encrypted. The ResponseEncryptAop aspect has the highest precedence ( @Order(0)) and surrounds the method execution. After the original method returns, it retrieves the AES key that was placed in the request parameters during decryption, encrypts the string representation of the return value with AESUtil.encryptToBase64, and wraps the ciphertext in a standard response object R.ok. This makes encryption transparent to the controller code.
5. Testing and Demonstration
Several test classes illustrate the workflow: TbStudentEntity – a simple POJO used as request/response payload. TbStudentController – defines /demo/encryptPost and /demo/encryptGet endpoints, both annotated with @ResponseEncrypt. The controller logs the extracted AES key and payload to show that decryption succeeded. UtilsTestClass – generates random AES keys, RSA key pairs, encrypts sample JSON data for POST (Base64) and GET (Hex) scenarios, and prints the resulting ciphertexts and RSA‑encrypted keys.
Additional JUnit tests verify the correctness of AES/RSA encryption, Hex/Base64 conversions, and the end‑to‑end decryption of responses.
The article also includes example HTTP requests (headers, JSON bodies, query strings) and the corresponding encrypted responses, demonstrating that after the filter and AOP processing the application behaves exactly like a normal Spring Boot service while all payloads remain confidential on the wire.
6. Takeaways
By combining hybrid AES‑RSA encryption, a request‑wrapping filter, and an AOP‑based response encryptor, developers can achieve end‑to‑end confidentiality without changing existing controller logic. The approach is modular, reusable, and fits naturally into the Spring Boot filter‑chain and aspect‑oriented programming model.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
