This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-jdkim.git
commit b2ac4bdba5e0873b19aba94c5b1a8103eeca17f7 Author: Benoit TELLIER <[email protected]> AuthorDate: Fri Oct 30 08:36:15 2026 +0100 [FIX] PublicKeyRecordImpl: report an undecodable p= as IllegalStateException getPublicKey() let the IllegalArgumentException of the base64 decoder escape: the verifier then reported a misleading "Invalid signature record", and direct callers got an undeclared exception. A key folded inside its padding ("= =") was rejected as well. p= is now decoded with FWS removed and decoding errors are rethrown as IllegalStateException, like the other key errors. Co-Authored-By: Claude Opus 5.5 <[email protected]> --- .../apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java | 7 ++++--- .../java/org/apache/james/jdkim/tagvalue/TagValue.java | 11 +++++++++++ .../java/org/apache/james/jdkim/PublicKeyRecordTest.java | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java b/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java index d482eab..8366fbc 100644 --- a/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java +++ b/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java @@ -28,7 +28,6 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; -import java.util.Base64; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -177,8 +176,7 @@ public class PublicKeyRecordImpl extends TagValue implements PublicKeyRecord { */ public PublicKey getPublicKey() { try { - String p = getValue("p").toString(); - byte[] key = Base64.getMimeDecoder().decode(p.getBytes()); + byte[] key = decodeBase64(getValue("p")); KeyFactory keyFactory; keyFactory = KeyFactory.getInstance(getValue("k").toString()); X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(key); @@ -191,6 +189,9 @@ public class PublicKeyRecordImpl extends TagValue implements PublicKeyRecord { } catch (InvalidKeySpecException e) { throw new IllegalStateException("Invalid key spec: " + e.getMessage()); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("Invalid key encoding: " + + e.getMessage()); } } diff --git a/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java b/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java index 68562a5..400a06b 100644 --- a/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java +++ b/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java @@ -46,6 +46,7 @@ public class TagValue { private static final Pattern valuePattern = Pattern.compile("^(?:" + tval + "(?:(?:(?:\r\n)?[\t ])++" + tval + ")*+)?$"); private static final Pattern b64PadFixPattern = Pattern.compile("=(\r?\n\\s*)=$"); + private static final Pattern fwsPattern = Pattern.compile("[ \t\r\n]"); // we may use a TreeMap because we may need to know original order. private final Map<String, CharSequence> tagValues; @@ -230,6 +231,16 @@ public class TagValue { ); } + /** + * Decodes a base64string (RFC 6376 section 2.8): FWS may appear anywhere, + * every other character must belong to the base64 alphabet. + * + * @throws IllegalArgumentException if the value is not valid base64 + */ + protected static byte[] decodeBase64(CharSequence value) { + return Base64.getDecoder().decode(fwsPattern.matcher(value).replaceAll("")); + } + protected void setValue(String tag, String value) { stringRepresentation = null; tagValues.put(tag, value); diff --git a/main/src/test/java/org/apache/james/jdkim/PublicKeyRecordTest.java b/main/src/test/java/org/apache/james/jdkim/PublicKeyRecordTest.java index 2059d4f..2ce3666 100644 --- a/main/src/test/java/org/apache/james/jdkim/PublicKeyRecordTest.java +++ b/main/src/test/java/org/apache/james/jdkim/PublicKeyRecordTest.java @@ -19,6 +19,8 @@ package org.apache.james.jdkim; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.Base64; import org.apache.james.jdkim.api.PublicKeyRecord; import org.apache.james.jdkim.tagvalue.PublicKeyRecordImpl; import org.junit.Assert; @@ -260,4 +262,18 @@ public class PublicKeyRecordTest { assertFalse(pkr.isDenySubdomains()); } + @Test + public void malformedKeyShouldThrowIllegalStateException() { + PublicKeyRecordImpl record = new PublicKeyRecordImpl("v=DKIM1; k=rsa; p=AAAAA"); + + assertThatThrownBy(record::getPublicKey).isInstanceOf(IllegalStateException.class); + } + + @Test + public void foldedKeyShouldDecode() throws Exception { + String key = Base64.getEncoder().encodeToString(TestKeys.publicKey.getEncoded()); + String folded = key.substring(0, 10) + "\r\n " + key.substring(10, key.length() - 1) + " " + key.substring(key.length() - 1); + + assertEquals(TestKeys.publicKey, new PublicKeyRecordImpl("v=DKIM1; k=rsa; p=" + folded).getPublicKey()); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
