[ 
https://issues.apache.org/jira/browse/CODEC-279?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17019527#comment-17019527
 ] 

Alex Herbert commented on CODEC-279:
------------------------------------

Your stack trace is from codec 1.13. That had a bug in the validation of final 
bits so I will not cover that here.

My working shows that the exception is expected:
{noformat}
Decode blocks of 4 characters as 4 * 6 bits = 24-bits = 3 bytes

Trailing characters are decoded as:
1 =  6 bits => Ignored
2 = 12 bits => 8-bits discard 4
3 = 18 bits => 16-bits discard 2

The discarded bits are now required to be zero.

86 / 4 = 21    remainder   2

Last two characters decoded to base64:
j = 35 = 100011
E =  4 = 000100
Last 12-bits = 100011 000100 
             = 10001100   0100
This has 4 trailing bits which should be zeros but they are 0100
{noformat}
Thus this is expected to throw.

Can you change the final character to 'A' and see if it still throws. I expect 
this to be fine. Then you can check that Codec returns the same bytes as the 
java.util.Base64 class.

You may ask what about 6 trailing bits when there is 1 trailing character? This 
is currently not validated. In [Codec-270] I suggested throwing an exception 
given that there should never be 6-bits left-over when decoding to 8-bit bytes. 
This would indicate a bad or truncated encoding. However this extra validation 
was not added and there is a comment in the code to determine if it should be 
validated.

The validation of trailing bytes was to ensure a round trip of 
decoding->encoding bytes produces the same bytes (see [Codec-134]). So given 
this argument I think that validation could be made more strict given that both 
Base32 and Base64 allow an entire single trailing character to be discarded but 
will throw if there are multiple trailing characters and the trailing bits to 
be discarded are non-zero.

 

> Base64.decode fails on Java11 for certain valid base 64 encoded String
> ----------------------------------------------------------------------
>
>                 Key: CODEC-279
>                 URL: https://issues.apache.org/jira/browse/CODEC-279
>             Project: Commons Codec
>          Issue Type: Bug
>    Affects Versions: 1.13, 1.14
>         Environment: Java 11
>            Reporter: Christian Kleinbölting
>            Priority: Major
>
> The following lines will fail for a specific String:
> {code:java}
> String sharedSecret = 
> "cannot_share_the_affected_string_because_it_is_a_secret";
> byte[] bytes = 
> org.apache.commons.codec.binary.Base64.decodeBase64(sharedSecret); {code}
> The exception I get is
> {code:java}
> java.lang.IllegalArgumentException: Last encoded character (before the 
> paddings if any) is a valid base 64 alphabet but not a possible value  at 
> org.apache.commons.codec.binary.Base64.validateCharacter(Base64.java:798)
>       at org.apache.commons.codec.binary.Base64.decode(Base64.java:472)
>       at 
> org.apache.commons.codec.binary.BaseNCodec.decode(BaseNCodec.java:412)
>       at 
> org.apache.commons.codec.binary.BaseNCodec.decode(BaseNCodec.java:395)
>       at org.apache.commons.codec.binary.Base64.decodeBase64(Base64.java:694)
>       at 
> com.rbmhtechnology.oidc.provider.spi.grants.jwt.thirdparty.rest.FooTest.foo(FooTest.java:17)
>       at 
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>       at 
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
>       at 
> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>       at java.base/java.lang.reflect.Method.invoke(Method.java:566)
>       at 
> org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
>       at 
> org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
>       at 
> org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
>       at 
> org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
>       at 
> org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
>       at 
> org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
>       at 
> org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
>       at 
> org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
>       at 
> org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
>       at 
> org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
>       at 
> org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
>       at 
> org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
>       at 
> org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
>       at 
> org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
>       at 
> org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
>       at 
> org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
>       at 
> org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
>       at 
> org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
>       at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
>       at 
> org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
>       at 
> org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
>       at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
>       at 
> org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
>       at 
> org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
>       at 
> org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
>       at 
> org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
>       at 
> org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
>       at 
> org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
>       at 
> org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
>       at 
> org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
>       at 
> org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
>       at 
> org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
>       at 
> org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
>       at 
> org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
>       at 
> com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
>       at 
> com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
>       at 
> com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
>       at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)Process 
> finished with exit code 255
>  {code}
> Using Java 11 (AdoptOpenJDK 13.0.1.hs-adpt). The same code will pass with 
> Java 8 (AdoptOpenJDK 8.0.232.hs-adpt) and return the expected bytes.
> Unfortunately I was not able to construct another String which will make 
> these lines throw an exception other than the String I have and that is a 
> secret I cannot share. {{java.util.Base64.getDecoder().decode(String)}} is 
> able to decode the affected String though.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to