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 61801be2bf6632ecd33f997cb4062649e7deb0b2
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Oct 30 08:44:04 2026 +0100

    [FIX] SignatureRecord: empty the b= tag itself in toUnsignedString()
    
    toUnsignedString() emptied the first "b=" substring of the field, so a
    tag such as xb=1, an h= entry holding "b=" or a DKIM-Quoted-Printable
    value with "b=XX" before the real b= tag was emptied instead, and the
    signature value stayed in the hashed data. Valid signatures then failed,
    including jDKIM's own when the template had such a tag.
    
    The tag-list is now walked tag by tag and only the value of the tag
    named b is deleted, with the whitespace around it.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../james/jdkim/tagvalue/SignatureRecordImpl.java    |  2 +-
 .../jdkim/tagvalue/SignatureRecordTemplate.java      |  2 +-
 .../org/apache/james/jdkim/tagvalue/TagValue.java    | 20 ++++++++++++++++++++
 .../test/java/org/apache/james/jdkim/DKIMTest.java   | 12 ++++++++++++
 .../apache/james/jdkim/SignatureRecordImplTest.java  |  8 ++++++++
 5 files changed, 42 insertions(+), 2 deletions(-)

diff --git 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
index 75104e9..5f42991 100644
--- 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
+++ 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
@@ -336,6 +336,6 @@ public class SignatureRecordImpl extends TagValue 
implements SignatureRecord {
     }
 
     public String toUnsignedString() {
-        return toString().replaceFirst("b=[^;]*", "b=");
+        return emptyTagValue(toString(), "b");
     }
 }
diff --git 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
index e7f27ee..e4c2594 100644
--- 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
+++ 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
@@ -325,6 +325,6 @@ public class SignatureRecordTemplate extends TagValue 
implements SignatureRecord
     }
 
     public String toUnsignedString() {
-        return toString().replaceFirst("b=[^;]*", "b=");
+        return emptyTagValue(toString(), "b");
     }
 }
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 991a854..3782c4e 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
@@ -237,6 +237,26 @@ public class TagValue {
         return 
Base64.getDecoder().decode(fwsPattern.matcher(value).replaceAll(""));
     }
 
+    /**
+     * Deletes the value of a tag, with the whitespace around it, and leaves 
the
+     * rest of the tag-list as is (RFC 6376 section 3.7 for b=).
+     */
+    protected static String emptyTagValue(String tagList, String tag) {
+        int start = 0;
+        while (start <= tagList.length()) {
+            int end = tagList.indexOf(';', start);
+            if (end == -1)
+                end = tagList.length();
+            int equal = tagList.indexOf('=', start);
+            if (equal != -1 && equal < end
+                    && 
tag.equals(fwsPattern.matcher(tagList.subSequence(start, 
equal)).replaceAll(""))) {
+                return tagList.substring(0, equal + 1) + 
tagList.substring(end);
+            }
+            start = end + 1;
+        }
+        return tagList;
+    }
+
     protected void setValue(String tag, String value) {
         stringRepresentation = null;
         tagValues.put(tag, value);
diff --git a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java 
b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
index ffd45c5..1a3c219 100644
--- a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
@@ -289,4 +289,16 @@ public class DKIMTest {
 
         assertThat(verifier.verify(in)).hasSize(1);
     }
+
+    @Test
+    public void signatureWithATagNameEndingInBShouldVerify() throws Exception {
+        DKIMSigner signer = new DKIMSigner("v=1; a=rsa-sha256; c=simple; 
d=messiah.edu; h=from; s=selector2; ab=x;", TestKeys.privateKey);
+        byte[] mail = "From: 
[email protected]\r\n\r\nbody\r\n".getBytes(StandardCharsets.US_ASCII);
+        String field = signer.sign(new ByteArrayInputStream(mail));
+
+        List<SignatureRecord> verified = verifier.verify(new 
SequenceInputStream(
+                new ByteArrayInputStream((field + 
"\r\n").getBytes(StandardCharsets.US_ASCII)), new ByteArrayInputStream(mail)));
+
+        assertThat(verified).hasSize(1);
+    }
 }
diff --git 
a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java 
b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java
index 6d54bf4..4312957 100644
--- a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java
@@ -279,4 +279,12 @@ public class SignatureRecordImplTest {
         assertThat(record.getBodyHash()).hasSize(32);
         assertThat(record.getSignature()).containsExactly(0);
     }
+
+    @Test
+    public void toUnsignedStringShouldEmptyOnlyTheBTag() {
+        assertThat(new SignatureRecordImpl("v=1; a=rsa-sha256; d=example.com; 
s=s; h=from; bh=AA==; xb=1; b=AA==").toUnsignedString())
+                .isEqualTo("v=1; a=rsa-sha256; d=example.com; s=s; h=from; 
bh=AA==; xb=1; b=");
+        assertThat(new SignatureRecordImpl("v=1; b = AA\r\n BB ; a=rsa-sha256; 
d=example.com; s=s; h=from:b=:x; bh=AA==").toUnsignedString())
+                .isEqualTo("v=1; b =; a=rsa-sha256; d=example.com; s=s; 
h=from:b=:x; bh=AA==");
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to