Skip to content

Commit 9d26f12

Browse files
committed
Add an example of Base64 encoding that failed with java.util.Base64
Revert usage to Apache Commons Codec (dependency by OpenSaml)
1 parent 22c2220 commit 9d26f12

File tree

3 files changed

+75
-5
lines changed

3 files changed

+75
-5
lines changed

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2Utils.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.security.saml2.provider.service.servlet.filter;
1818

19+
import org.apache.commons.codec.binary.Base64;
1920
import org.springframework.security.saml2.Saml2Exception;
2021
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
2122
import org.springframework.util.StringUtils;
@@ -24,7 +25,6 @@
2425

2526
import java.io.ByteArrayOutputStream;
2627
import java.io.IOException;
27-
import java.util.Base64;
2828
import java.util.HashMap;
2929
import java.util.Map;
3030
import java.util.zip.Deflater;
@@ -44,15 +44,14 @@
4444
final class Saml2Utils {
4545

4646
private static final char PATH_DELIMITER = '/';
47-
private static Base64.Encoder ENCODER = Base64.getEncoder();
48-
private static Base64.Decoder DECODER = Base64.getDecoder();
47+
private static org.apache.commons.codec.binary.Base64 BASE64 = new Base64(0, new byte[]{'\n'});
4948

5049
static String encode(byte[] b) {
51-
return ENCODER.encodeToString(b);
50+
return BASE64.encodeAsString(b);
5251
}
5352

5453
static byte[] decode(String s) {
55-
return DECODER.decode(s);
54+
return BASE64.decode(s);
5655
}
5756

5857
static byte[] deflate(String s) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.saml2.provider.service.servlet.filter;
18+
19+
import org.apache.commons.codec.binary.Base64;
20+
import org.junit.Test;
21+
import org.springframework.core.io.ClassPathResource;
22+
import org.springframework.util.StreamUtils;
23+
import org.springframework.web.util.UriUtils;
24+
25+
import java.io.IOException;
26+
import java.nio.charset.StandardCharsets;
27+
28+
import static java.nio.charset.StandardCharsets.UTF_8;
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
public class Saml2UtilsTests {
32+
33+
private static Base64 UNCHUNKED_ENCODER = new Base64(0, new byte[]{'\n'});
34+
private static final Base64 CHUNKED_ENCODER = new Base64(76, new byte[] { '\n' });
35+
36+
@Test
37+
public void decodeWhenUsingApacheCommonsBase64ThenXmlIsValid() throws Exception {
38+
String responseUrlDecoded = getSsoCircleEncodedXml();
39+
String xml = new String(UNCHUNKED_ENCODER.decode(responseUrlDecoded.getBytes(UTF_8)), UTF_8);
40+
validateSsoCircleXml(xml);
41+
}
42+
43+
@Test
44+
public void decodeWhenUsingApacheCommonsBase64ChunkedThenXmlIsValid() throws Exception {
45+
String responseUrlDecoded = getSsoCircleEncodedXml();
46+
String xml = new String(CHUNKED_ENCODER.decode(responseUrlDecoded.getBytes(UTF_8)), UTF_8);
47+
validateSsoCircleXml(xml);
48+
}
49+
50+
@Test
51+
public void decodeWhenUsingSamlUtilsBase64ThenXmlIsValid() throws Exception {
52+
String responseUrlDecoded = getSsoCircleEncodedXml();
53+
String xml = new String(Saml2Utils.decode(responseUrlDecoded), UTF_8);
54+
validateSsoCircleXml(xml);
55+
}
56+
57+
private void validateSsoCircleXml(String xml) {
58+
assertThat(xml)
59+
.contains("InResponseTo=\"ARQ9a73ead-7dcf-45a8-89eb-26f3c9900c36\"")
60+
.contains(" ID=\"s246d157446618e90e43fb79bdd4d9e9e19cf2c7c4\"")
61+
.contains("<saml:Issuer>https://idp.ssocircle.com</saml:Issuer>");
62+
}
63+
64+
private String getSsoCircleEncodedXml() throws IOException {
65+
ClassPathResource resource = new ClassPathResource("saml2-response-sso-circle.encoded");
66+
String response = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
67+
return UriUtils.decode(response, UTF_8);
68+
}
69+
70+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6%0D%0AcHJvdG9jb2wiIElEPSJzMjQ2ZDE1NzQ0NjYxOGU5MGU0M2ZiNzliZGQ0ZDllOWUxOWNmMmM3YzQi%0D%0AIEluUmVzcG9uc2VUbz0iQVJROWE3M2VhZC03ZGNmLTQ1YTgtODllYi0yNmYzYzk5MDBjMzYiIFZl%0D%0AcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE5LTEyLTE5VDE3OjMxOjI0WiIgRGVzdGluYXRp%0D%0Ab249Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9sb2dpbi9zYW1sMi9zc28vc3NvY2lyY2xlIj48c2Ft%0D%0AbDpJc3N1ZXIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlv%0D%0AbiI%2BaHR0cHM6Ly9pZHAuc3NvY2lyY2xlLmNvbTwvc2FtbDpJc3N1ZXI%2BPHNhbWxwOlN0YXR1cyB4%0D%0AbWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj4KPHNhbWxw%0D%0AOlN0YXR1c0NvZGUgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90%0D%0Ab2NvbCIgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyI%2B%0D%0ACjwvc2FtbHA6U3RhdHVzQ29kZT4KPC9zYW1scDpTdGF0dXM%2BPHNhbWw6QXNzZXJ0aW9uIHhtbG5z%0D%0AOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJzMjg4MjBl%0D%0AZWQyY2QyNWQ0Y2I3ZTAwOTg1OTM4OGMzMWU1ZjFlZWZhOTciIElzc3VlSW5zdGFudD0iMjAxOS0x%0D%0AMi0xOVQxNzozMToyNFoiIFZlcnNpb249IjIuMCI%2BCjxzYW1sOklzc3Vlcj5odHRwczovL2lkcC5z%0D%0Ac29jaXJjbGUuY29tPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8v%0D%0Ad3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8%2BCjxkczpDYW5vbmlj%0D%0AYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwt%0D%0AZXhjLWMxNG4jIi8%2BCjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3Lncz%0D%0ALm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KPGRzOlJlZmVyZW5jZSBVUkk9IiNzMjg4%0D%0AMjBlZWQyY2QyNWQ0Y2I3ZTAwOTg1OTM4OGMzMWU1ZjFlZWZhOTciPgo8ZHM6VHJhbnNmb3Jtcz4K%0D%0APGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNp%0D%0AZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BCjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8v%0D%0Ad3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KPC9kczpUcmFuc2Zvcm1zPgo8ZHM6%0D%0ARGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2ln%0D%0AI3NoYTEiLz4KPGRzOkRpZ2VzdFZhbHVlPnc2SkVMMGRNNGVuT0EwWmQ5Q0tpbXNwTHpEUT08L2Rz%0D%0AOkRpZ2VzdFZhbHVlPgo8L2RzOlJlZmVyZW5jZT4KPC9kczpTaWduZWRJbmZvPgo8ZHM6U2lnbmF0%0D%0AdXJlVmFsdWU%2BCkg1TGh5VUZITnhzUUdaaWZjbmpkVUV1cnVCSTJ5UkpoYXJJQ1hoUkU5c1dBYkg3%0D%0ASWxGUWx1S2NmV3hGbjNpNEdIdzZRZDg3cDVkL3EKUE5ZTmptTVpUemU2bENIS2dmenZmRExObjNU%0D%0AM0ZhWXFtYkxvTkJUU1YxMTk1Wmgxa1FhdFIwY1NHZXF3NHJtbGR0enZvRzZrNk92YwovRUk5U05i%0D%0ARFJJYURBOW1lbXdEOG0wczVoSUxMb09uRGNQb2lxTHNKR3laYmR2YVZ3RVlnYS9XMitmSmNpTWNB%0D%0ARDViMGdpOWpab05oCko2dnhIcVpwaEg1dm9MZTNFdGVPQ083TTFhMTVmNk9jWU9MeXFYbFFUdytu%0D%0AeElsYnBEeG03ZTQ4VVljL1htYmRwMUsvNlMwOFdmZWkKNFZLL3ZSZjVaUnZ4ckZ4V2ZrRlRGYlN2%0D%0AVkVlOFpURWM3NlEzWkE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz4KPGRzOlg1%0D%0AMDlEYXRhPgo8ZHM6WDUwOUNlcnRpZmljYXRlPgpNSUlFWXpDQ0FrdWdBd0lCQWdJRElBWm1NQTBH%0D%0AQ1NxR1NJYjNEUUVCQ3dVQU1DNHhDekFKQmdOVkJBWVRBa1JGTVJJd0VBWURWUVFLCkRBbFRVMDlE%0D%0AYVhKamJHVXhDekFKQmdOVkJBTU1Ba05CTUI0WERURTJNRGd3TXpFMU1ETXlNMW9YRFRJMk1ETXdO%0D%0AREUxTURNeU0xb3cKUFRFTE1Ba0dBMVVFQmhNQ1JFVXhFakFRQmdOVkJBb1RDVk5UVDBOcGNtTnNa%0D%0AVEVhTUJnR0ExVUVBeE1SYVdSd0xuTnpiMk5wY21OcwpaUzVqYjIwd2dnRWlNQTBHQ1NxR1NJYjNE%0D%0AUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUNBd1dKeU9ZaFltV1pGMlRKdm0xVnlaY2NzCjNaSjBU%0D%0Ac05jb2F6cjJwVFdjWThXVFJiSVY5ZDA2ellqbmd2V2lieWl5bGV3R1hjWU9OQjEwNlpOVWROZ3Jt%0D%0ARmQ1MTk0V3N5eDZiUHYKbmpaRUVSbnk5TE9mdXdRYXFEWWVLaEk2Yyt2ZVhBcG5PZnNZMjZ1OUxx%0D%0AYjlzZ2E5Sm5Da1VHUmFvVnJBVk0zeWZnaHYvQ2cvUUVnKwpJNlNWRVM3NXRLZGNMRFR0L0Z3bUFZ%0D%0AREVCVjhsNTJiY01ETkYrSld0QXVldEk5L2RXQ0JlOVZUQ2FzQXIyRnh3MVpZVEFpcUdJOXNXCjRr%0D%0AV1MyQXBlZGJxc2dIM3FxTWxQQTd0ZzlpS3k4WXcvZGVFbjBxUUl4OEdsVm5RRnBEZ3pHOWsrandC%0D%0Ab2ViQVlmR3ZNY08vQkRYRDIKcGJXVE4rRHZiVVJsQWdNQkFBR2plekI1TUFrR0ExVWRFd1FDTUFB%0D%0Ad0xBWUpZSVpJQVliNFFnRU5CQjhXSFU5d1pXNVRVMHdnUjJWdQpaWEpoZEdWa0lFTmxjblJwWm1s%0D%0AallYUmxNQjBHQTFVZERnUVdCQlFoQW1DZXdFN2FvbkF2eUpmakltQ1JaRHRjY1RBZkJnTlZIU01F%0D%0ACkdEQVdnQlRBMW5FQSswemE2cHBMSXRrT1g1eUVwOGNRYVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9D%0D%0AQWdFQUFoQzUvV3NGOXp0SkhnbysKeDlLVjlicVZTME1tc2dwRzI2eU9BcUZZd09TUG1VdVltSm1I%0D%0AZ21LR2pLcmoxZmRDSU50emNCSEZGQkMxbWFHSjMzbE1rMmJNMlRIeAoyMi9POTNmNFJGbkZhYjd0%0D%0AMjNqUkZjRjBhbVFVT3NEdmx0Zkp3N1hDYWw4SmRnUFVnNlROQzRGeTlYWXYwT0FIYzNvRHAzdmwx%0D%0AWWo4Ci8xcUJnNlJjMzlrZWhtRDV2OFNLWW1wRTd5Rkt4REYxb2w5REtERy9MdkNsU3ZudVZQMGI0%0D%0AQldkQkFBOWFKU0Z0ZE5HZ0V2cEVVcUcKa0oxb3NMVnFDTXZTWXNVdEhtYXBhWDNoaU05UmJYMzhq%0D%0Ac1Nnc2w0NFJhcjVJb2M3S1hPT1pGR2ZFS3l5VXF1Y1lwaldDT1hKRUxBVgpBenA3WFR2QTJxNTV1%0D%0AMzFoTzB3OFl4NHVFUUtsbXhEdVpteHBNejRFV0FSeWpIU0F1REtFVzFSSnZVcjYrNXVBOXFlT0t4%0D%0ATGlLTjFqCm82ZVdBY2w2V3I5TXJlWFI5a0ZwUzZrSGxsZmRWU3JKRVM0U1QwdWgxSnA0RVlnbWl5%0D%0ATW1GQ2JVcEtYaWZwc05XQ0xEZW5FM2hsbEYKMCtxM3dJZHUrNFA4MlJJTTcxbjdxVmduRG5LMjl3%0D%0AbkxoSERhdDlya0M2MkNJYm9ucGtWWW1uUmVYMGp6ZSs3dHdSYW5KT01DSitsRgpnMTZCRHZCY0c4%0D%0AdTBuL3dJRGtISGl0Qkk3YlUxazZjNkR5ZExRKzY5aDhTQ282c085WXVEKy8zeEFHS2FkNEltWjZ2%0D%0AVHdsQjR6RENwCnU2WWdRV29jV1JYRStWa09iK1JCZnZQNzU1UFVhTGZMNjNBRlZscE9uRXBJaW81%0D%0AKytVak5KUnVQdUFBPQo8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KPC9kczpYNTA5RGF0YT4KPC9kczpL%0D%0AZXlJbmZvPgo8L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0Pgo8c2FtbDpOYW1lSUQgRm9ybWF0%0D%0APSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCIg%0D%0ATmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9pZHAuc3NvY2lyY2xlLmNvbSI%2BZmhhbmlrcGl2b3RhbDwv%0D%0Ac2FtbDpOYW1lSUQ%2BPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpu%0D%0AYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPgo8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0%0D%0AYSBJblJlc3BvbnNlVG89IkFSUTlhNzNlYWQtN2RjZi00NWE4LTg5ZWItMjZmM2M5OTAwYzM2IiBO%0D%0Ab3RPbk9yQWZ0ZXI9IjIwMTktMTItMTlUMTc6NDE6MjRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2Nh%0D%0AbGhvc3Q6ODA4MC9sb2dpbi9zYW1sMi9zc28vc3NvY2lyY2xlIi8%2BPC9zYW1sOlN1YmplY3RDb25m%0D%0AaXJtYXRpb24%2BCjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE5%0D%0ALTEyLTE5VDE3OjIxOjI0WiIgTm90T25PckFmdGVyPSIyMDE5LTEyLTE5VDE3OjQxOjI0WiI%2BCjxz%0D%0AYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24%2BCjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9sb2NhbGhvc3Q6%0D%0AODA4MC9zYW1sMi9zZXJ2aWNlLXByb3ZpZGVyLW1ldGFkYXRhL3Nzb2NpcmNsZTwvc2FtbDpBdWRp%0D%0AZW5jZT4KPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24%2BCjwvc2FtbDpDb25kaXRpb25zPgo8c2Ft%0D%0AbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTktMTItMTlUMTY6Mzg6NTFaIiBTZXNz%0D%0AaW9uSW5kZXg9InMyN2NkMjI4ZTIzMjNlZDM5MzM3YjM1NDcyODk5MmQ5ODRiMDNiYzEwMSI%2BPHNh%0D%0AbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1l%0D%0Aczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1s%0D%0AOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ%2BPC9zYW1sOkF1dGhuU3Rh%0D%0AdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iRW1h%0D%0AaWxBZGRyZXNzIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5v%0D%0AcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxT%0D%0AY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZoYW5pa0BwaXZvdGFsLmlvPC9z%0D%0AYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU%2BPHNhbWw6QXR0cmlidXRlIE5hbWU9%0D%0AIlVzZXJJRCI%2BPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3Jn%0D%0ALzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2No%0D%0AZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5maGFuaWtwaXZvdGFsPC9zYW1sOkF0%0D%0AdHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU%2BPHNhbWw6QXR0cmlidXRlIE5hbWU9IkZpcnN0%0D%0ATmFtZSI%2BPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIw%0D%0AMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1h%0D%0ALWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5GaWxpcDwvc2FtbDpBdHRyaWJ1dGVWYWx1%0D%0AZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJMYXN0TmFtZSI%2BPHNhbWw6%0D%0AQXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1h%0D%0AIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4%0D%0Ac2k6dHlwZT0ieHM6c3RyaW5nIj5IYW5pazwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0%0D%0AcmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPC9zYW1sOkFzc2VydGlvbj48L3NhbWxw%0D%0AOlJlc3BvbnNlPg%3D%3D

0 commit comments

Comments
 (0)