[cryptor] support AES and SM4 encryption strategies - #6531
Conversation
Aias00
left a comment
There was a problem hiding this comment.
Reviewed #6531. No blockers. Two should-fix items and a few nits.
Should fix
-
Static IV in CBC (security).
AbstractCbcCryptorStrategyreuses the configured IV for every call (buildCipher, lines 62-71), and the documented key format isbase64(secret):base64(iv)— i.e. one fixed (key, IV) pair per rule. Reusing a (key, IV) in CBC leaks first-block plaintext equality (identical JSON prefixes → identical first ciphertext blocks across requests) and opens chosen-plaintext paths. I know this matches the existingshenyu-common/AesUtilsposture, but for a security plugin it's worth not silently propagating. At minimum, add a Javadoc warning that the IV must be unique per deployment/rule; ideally add a random-IV-per-message variant that prepends the IV to the ciphertext. -
No SPI-wiring regression test. Both new tests instantiate the strategy directly (
new AesStrategy()/new Sm4Strategy()), so the META-INF SPI registration that the plugin actually depends on (CryptorStrategyFactory.newInstance("aes")→ExtensionLoader.getJoin) is never exercised. If theaes=/sm4=line or the@Joinannotation is dropped, these tests still pass and the plugin fails at runtime (silently — the factory catches and returnsnull). Please add a test that loads viaCryptorStrategyFactory.newInstance("aes")/"sm4"and round-trips.
Nits
-
Key-format divergence from
AesUtils(raw UTF-8 string key/iv vsbase64(secret):base64(iv)) is unmentioned and will confuse operators using the same secret acrossshenyu.aes.secret.*and the cryptoraesstrategy. A Javadoc cross-reference would help. -
Negative tests cover separator/format errors only — no non-base64 content or wrong byte-length (15-byte AES secret, 17-byte SM4 key) cases.
-
AesStrategyTest/Sm4StrategyTestvs the existingRSAStrategyTestnaming — trivial style divergence.
The crypto wiring itself (explicit BC provider via Cipher.getInstance(transformation, "BC"), PKCS7Padding for AES/SM4, per-call Cipher, standard Base64 output decoded by the factory's MIME decoder) is correct, and the @Join/SPI file additions are right. The static-block provider registration with a getProvider null-guard is actually cleaner than AesUtils's unconditional addProvider per call.
| private Cipher buildCipher(final int mode, final String secretBase64, final String ivBase64) throws Exception { | ||
| byte[] secret = Base64.getMimeDecoder().decode(secretBase64); | ||
| byte[] iv = Base64.getMimeDecoder().decode(ivBase64); | ||
| Cipher cipher = Cipher.getInstance(getTransformation(), BouncyCastleProvider.PROVIDER_NAME); |
…ative test cases Should-fix apache#1: Add Javadoc security warning on IV reuse in CBC mode, including cross-reference to AesUtils key format divergence (nit apache#3). Should-fix apache#2: Add CryptorStrategyFactorySpiTest that loads strategies via CryptorStrategyFactory.newInstance() (exercising META-INF SPI + ExtensionLoader.getJoin), not just direct instantiation. Nit apache#4: Add negative tests for wrong byte-length keys (15-byte AES, 18-byte SM4) and non-base64 content. apache#6531
|
Thanks @Aias00 for the thorough review. All items addressed in the latest push: Should-fix #1 (IV reuse Javadoc): Added security warning to Should-fix #2 (SPI-wiring regression test): Added Nit #3 (key format divergence): Added cross-reference in the same Javadoc pointing to Nit #4 (negative test coverage): Added 3 new negative tests: AES 15-byte key (wrong length), SM4 18-byte key (wrong length), and non-base64 key content. |
|
Strong test coverage (parameterized round-trip incl. CJK/JSON, wrong key-length, non-base64, SPI loadability). The SPI registration and the BouncyCastle provider guard are correct. One security design issue I think should be addressed before merge: CBC with a fixed IV — IV reused across all messages. CBC without authentication (malleable). Minor: key material ( |
The cryptor plugin shipped only RSA out of the box. Add AesStrategy and Sm4Strategy so the CryptorStrategy SPI covers symmetric ciphers too. A shared AbstractCbcCryptorStrategy encapsulates the CBC wiring -- key parsing, SecretKeySpec/IvParameterSpec init and BouncyCastle provider registration -- while subclasses merely declare the transformation and algorithm name. Key convention: base64(secret):base64(iv), with a fixed AES|SM4/CBC/PKCS7Padding transformation. The CryptorStrategy interface, CryptorRuleHandler and admin stay untouched, so RSA and existing rules keep working. Tests: parameterized round-trip (ASCII/CJK/JSON payloads) plus invalid key-format cases. New strategy classes at 100% instruction coverage.
…ative test cases Should-fix apache#1: Add Javadoc security warning on IV reuse in CBC mode, including cross-reference to AesUtils key format divergence (nit apache#3). Should-fix apache#2: Add CryptorStrategyFactorySpiTest that loads strategies via CryptorStrategyFactory.newInstance() (exercising META-INF SPI + ExtensionLoader.getJoin), not just direct instantiation. Nit apache#4: Add negative tests for wrong byte-length keys (15-byte AES, 18-byte SM4) and non-base64 content. apache#6531
Replace CBC with a fixed IV by authenticated encryption throughout the cryptor plugin to close the IV-reuse (CWE-329) and malleability (CWE-1204) issues raised in review. AES/SM4: - Switch AES/CBC/PKCS7Padding and SM4/CBC/PKCS7Padding to GCM/NoPadding with a fresh 96-bit SecureRandom nonce per message and a 128-bit tag, emitted as base64(nonce || ciphertext || tag). Key format simplified to base64(secret) — GCM must never reuse a fixed IV, so the configured IV is removed and the legacy base64(secret):base64(iv) form is rejected. RSA: - Default 'rsa' strategy upgraded from PKCS#1 v1.5 to RSA/ECB/OAEPWithSHA-256AndMGF1Padding, with an explicit OAEPParameterSpec (MGF1 SHA-256) so the transformation is identical across JDKs/providers. - New 'rsa-pkcs1' strategy keeps PKCS#1 v1.5 for legacy/external peers that cannot speak OAEP. Shared logic factored into AbstractRsaStrategy. Misc: - Unify Base64 encoder/decoder; decrypt now decodes UTF-8 explicitly. - RSA test fixtures moved from 512-bit to 2048-bit (OAEP/SHA-256 cannot encrypt any plaintext under a 512-bit key). - Refactor CryptorRequestPluginTest to shared key constants (DRY). https://github.com/apache/shenyu/pr/6531
a2da12d to
88b310d
Compare
|
Thanks @Aias00 — your points on IV reuse and missing authentication were spot on. Rather than documenting the risk, this push closes it. All three items addressed: 1. CBC IV reuse (CWE-329) → resolved by switching to GCM. 2. CBC malleability (CWE-1204) → resolved by GCM's authentication tag. 3. Base64 encoder/decoder asymmetry → fixed. Unified to RSA — PKCS#1 v1.5 → OAEP, with a PKCS#1 fallback for your non-regression concern.
Shared RSA logic is factored into Note: RSA test fixtures moved from 512-bit to 2048-bit — OAEP/SHA-256 physically cannot encrypt any plaintext under a 512-bit key ( The full cryptor module suite (40 tests) is green locally (main project install + integrated-test test-compile both pass), including the SPI-wiring path you flagged earlier. Would appreciate another look when you have time. |
Motivation
The cryptor plugin ships only RSA out of the box. Users needing symmetric ciphers (AES, SM4) have no built-in strategy today; SM4 is also required for Chinese national-standard (GM) compliance.
Modifications
AesStrategyandSm4Strategy(@Join) implementing theCryptorStrategySPI, registered asaesandsm4.AbstractCbcCryptorStrategyencapsulates CBC wiring: key parsing,SecretKeySpec/IvParameterSpecinit and BouncyCastle provider registration; subclasses only declare transformation + algorithm.base64(secret):base64(iv), fixedAES|SM4/CBC/PKCS7Padding.bcprov-jdk18onto the cryptor module.No breaking changes: the
CryptorStrategyinterface,CryptorRuleHandler, admin and existing RSA rules are untouched.Rule config example
strategyName=sm4,key=<base64-secret>:<base64-iv>Tests