惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
I
InfoQ
H
Help Net Security
GbyAI
GbyAI
博客园 - 叶小钗
Recent Announcements
Recent Announcements
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
Y
Y Combinator Blog
L
LangChain Blog
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
G
Google Developers Blog
Apple Machine Learning Research
Apple Machine Learning Research
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
博客园 - Franky
D
Docker
Jina AI
Jina AI
罗磊的独立博客

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Yeueuei
Charlie Fubon · 2026-06-22 · via DEV Community

Charlie Fubon

Good progress — down to 4 failures from the original pile. Let me trace each one precisely.

Failure 1 & 2 — RSAAdapterResponseTest empty XML tests:

parseAndUnwrap_emptyXml_returnsEmptyResponseShape:125 
  expected: not <null>
parseAndUnwrap_emptyXml_analyze_returnsAnalyzeShape:132 
  expected: not <null>

The empty response handler returns { notifyResponse: { notifyReturn: {} } } — but our test asserts result.get("notifyReturn") directly. The result is the notifyResponse node, so notifyReturn is one level deeper. Test assertion is wrong, not the production code.

Error 3 & 4 — RSACryptoUtilTest:

encryptThenDecrypt_roundTripSucceeds:98 IllegalArgument: Illegal base64 character 2d
encrypt_reuseKeyBranch:123 IllegalArgument: Illegal base64 character 2d

Character 2d is a hyphen -. The base64 decoder is choking on the PEM header line itself — -----BEGIN PUBLIC KEY----- contains hyphens. This means our stripped string still has header content. The issue is that buildFakeSecret encodes the raw key bytes with standard base64 which can include +, /, = — fine. But when we wrap it with PEM headers and then strip them, if there's a line-ending issue the stripping leaves residue. Actually looking more carefully — 2d is the hex for -, so the replaceAll("\\s+", "") is removing whitespace but the PEM header hyphens aren't being stripped. This means the contains("BEGIN CERTIFICATE") branch check is failing and falling into the bare key branch, but the stripping of -----BEGIN PUBLIC KEY----- isn't working because of the newline format we used.

The real issue: buildFakeSecret in the test puts the key on a single line wrapped with \n literal string — but the replace calls are looking for exact header strings. If there's any whitespace variation it fails.

All four fixes — make these changes:


Fix 1 — RSAAdapterResponseTest empty XML assertions. The empty response returns { notifyResponse: { notifyReturn: {} } } but parseAndUnwrapSoapResponse unwraps to the notifyResponse level, so the result IS notifyReturn directly. Wait — re-reading parseAndUnwrapSoapResponse: the empty path does:

ret.set(normalizedType + "Return", objectMapper.createObjectNode());
empty.set(normalizedType + "Response", ret);
return empty;

So it returns empty which is { notifyResponse: { notifyReturn: {} } }. The test asserts result.get("notifyReturn") — but notifyReturn is nested under notifyResponse. Fix the assertions:

@Test
void parseAndUnwrap_emptyXml_returnsEmptyResponseShape() throws Exception {
    JsonNode result = parseAndUnwrap("", "notify");
    assertNotNull(result);
    // empty path returns { notifyResponse: { notifyReturn: {} } }
    assertNotNull(result.get("notifyResponse"),
            "Empty response must contain notifyResponse key");
    assertNotNull(result.get("notifyResponse").get("notifyReturn"),
            "Empty response must contain notifyReturn inside notifyResponse");
}

@Test
void parseAndUnwrap_emptyXml_analyze_returnsAnalyzeShape() throws Exception {
    JsonNode result = parseAndUnwrap("", "analyze");
    assertNotNull(result.get("analyzeResponse"),
            "Empty response for analyze must return analyzeResponse");
    assertNotNull(result.get("analyzeResponse").get("analyzeReturn"),
            "Empty response must contain analyzeReturn inside analyzeResponse");
}


Fix 2 — RSACryptoUtilTest.buildFakeSecret. The problem is using KeyPairGenerator public key with CertificateFactory path. Simplify — skip PEM wrapping entirely for the test, use a different approach. Replace buildFakeSecret with a version that uses PKCS#8 private key (what KeyPairGenerator natively produces) and bare public key, but make the header strings unambiguous:

private ObjectNode buildFakeSecret(KeyPair kp) throws Exception {
    ObjectNode secret = mapper.createObjectNode();
    secret.put("currentVersion", "1");

    // Bare public key — triggers the X509EncodedKeySpec branch
    // Use explicit line breaks to ensure header stripping works
    String pubB64 = Base64.getMimeEncoder(64, new byte[]{'\n'})
            .encodeToString(kp.getPublic().getEncoded());
    secret.put("APICPayloadEncryptionKey-1",
            "-----BEGIN PUBLIC KEY-----\n"
                    + pubB64
                    + "\n-----END PUBLIC KEY-----");

    // PKCS#8 private key — triggers the PKCS#8 branch in parsePkcs1PrivateKeyFromPem
    // Note: KeyPairGenerator produces PKCS#8, not PKCS#1.
    // We test PKCS#1 parsing in a dedicated test below.
    // Here we need the decrypt path to work, so use PKCS#8 headers.
    String privB64 = Base64.getMimeEncoder(64, new byte[]{'\n'})
            .encodeToString(kp.getPrivate().getEncoded());
    secret.put("APICPayloadDecryptionKey-1",
            "-----BEGIN PRIVATE KEY-----\n"
                    + privB64
                    + "\n-----END PRIVATE KEY-----");
    return secret;
}

And update parsePkcs1PrivateKeyFromPem in RSACryptoUtil.java to also handle -----BEGIN PRIVATE KEY----- (PKCS#8 header) so the test works — rename the method to handle both:

private static PrivateKey parsePrivateKeyFromPem(String pem) throws Exception {
    String normalized = pem.replace("\\n", "\n").trim();
    if (normalized.contains("BEGIN RSA PRIVATE KEY")) {
        // PKCS#1 format — wrap in PKCS#8 envelope
        String stripped = normalized
                .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .replace("-----END RSA PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");
        byte[] pkcs1Der = Base64.getDecoder().decode(stripped);
        // ... same PKCS#8 wrapping code as before ...
        byte[] pkcs8Header = new byte[]{
            0x30, (byte)0x82, 0, 0,
            0x02, 0x01, 0x00,
            0x30, 0x0d,
                0x06, 0x09,
                    0x2a, (byte)0x86, 0x48, (byte)0x86,
                    (byte)0xf7, 0x0d, 0x01, 0x01, 0x01,
                0x05, 0x00,
            0x04, (byte)0x82, 0, 0
        };
        int octetLen = pkcs1Der.length;
        int totalLen = pkcs8Header.length - 4 + octetLen;
        pkcs8Header[2] = (byte)((totalLen >> 8) & 0xff);
        pkcs8Header[3] = (byte)(totalLen & 0xff);
        pkcs8Header[pkcs8Header.length - 2] = (byte)((octetLen >> 8) & 0xff);
        pkcs8Header[pkcs8Header.length - 1] = (byte)(octetLen & 0xff);
        byte[] pkcs8Der = new byte[pkcs8Header.length + pkcs1Der.length];
        System.arraycopy(pkcs8Header, 0, pkcs8Der, 0, pkcs8Header.length);
        System.arraycopy(pkcs1Der, 0, pkcs8Der, pkcs8Header.length, pkcs1Der.length);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(new PKCS8EncodedKeySpec(pkcs8Der));
    } else {
        // PKCS#8 format ("BEGIN PRIVATE KEY") — JDK handles natively
        String stripped = normalized
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");
        byte[] der = Base64.getDecoder().decode(stripped);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(new PKCS8EncodedKeySpec(der));
    }
}

Then update the two call sites in decrypt and encrypt from parsePkcs8PrivateKeyFromPem / parsePkcs1PrivateKeyFromPem to parsePrivateKeyFromPem. Delete the old method entirely.

Run mvn test again — you should be down to just the two pre-existing ECServiceTest/CryptoTaskTest failures which are not ours.