This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/jwsinputstream in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 5ea3d70c534916df97ea2bf4b2b57e036346d80d Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Thu Aug 27 10:08:47 2026 +0100 Fix bug in JwsInputStream and add tests --- .../cxf/rs/security/jose/jws/JwsInputStream.java | 3 +- .../rs/security/jose/jws/JwsInputStreamTest.java | 82 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java index 6225e35c834..53662ebba2b 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java @@ -21,7 +21,6 @@ package org.apache.cxf.rs.security.jose.jws; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; public class JwsInputStream extends FilterInputStream { private final JwsVerificationSignature signature; @@ -42,7 +41,7 @@ public class JwsInputStream extends FilterInputStream { public int read() throws IOException { int value = super.read(); if (value != -1) { - byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array(); + byte[] bytes = new byte[] {(byte)value}; signature.update(bytes, 0, bytes.length); } else { verify(); diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsInputStreamTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsInputStreamTest.java new file mode 100644 index 00000000000..53581dafdb4 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsInputStreamTest.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jws; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + +import org.junit.Test; + +import static org.junit.Assert.fail; + +public class JwsInputStreamTest { + + private static final byte[] KEY = "0123456789abcdef0123456789abcdef".getBytes(StandardCharsets.UTF_8); + private static final byte[] CONTENT = "signed multipart content".getBytes(StandardCharsets.UTF_8); + + @Test + public void testSingleByteReadVerifiesValidSignature() throws Exception { + byte[] signatureBytes = sign(CONTENT); + + JwsVerificationSignature vSig = createVerificationSignature(); + try (JwsInputStream jwsStream = + new JwsInputStream(new ByteArrayInputStream(CONTENT), vSig, signatureBytes, true)) { + // Reading one byte at a time must feed the exact content bytes into the + // signature and thus accept a valid signature at the end of the stream + while (jwsStream.read() != -1) { + // consume + } + } + } + + @Test + public void testTamperedContentIsRejected() throws Exception { + byte[] signatureBytes = sign(CONTENT); + + byte[] tampered = CONTENT.clone(); + tampered[0] ^= 0x01; + JwsVerificationSignature vSig = createVerificationSignature(); + try (JwsInputStream jwsStream = + new JwsInputStream(new ByteArrayInputStream(tampered), vSig, signatureBytes, true)) { + byte[] buf = new byte[8]; + try { + while (jwsStream.read(buf, 0, buf.length) != -1) { + // consume + } + fail("Failure expected on tampered content"); + } catch (JwsException ex) { + // expected + } + } + } + + private byte[] sign(byte[] content) { + HmacJwsSignatureProvider provider = new HmacJwsSignatureProvider(KEY, SignatureAlgorithm.HS256); + JwsSignature sig = provider.createJwsSignature(new JwsHeaders(SignatureAlgorithm.HS256)); + sig.update(content, 0, content.length); + return sig.sign(); + } + + private JwsVerificationSignature createVerificationSignature() { + HmacJwsSignatureVerifier verifier = new HmacJwsSignatureVerifier(KEY, SignatureAlgorithm.HS256); + return verifier.createJwsVerificationSignature(new JwsHeaders(SignatureAlgorithm.HS256)); + } +}
