Skip to content
This repository was archived by the owner on May 26, 2020. It is now read-only.

Commit 8c5a229

Browse files
committed
SANTUARIO-525 Made Base64 line length and line separator configurable, such that "
" in Base64 encoded elements can be avoided
1 parent af62090 commit 8c5a229

9 files changed

Lines changed: 443 additions & 44 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/.idea
2+
/*.iml
13
/.classpath
24
/.project
35
/target/

src/main/java/org/apache/xml/security/signature/XMLSignature.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,11 +645,6 @@ private void setSignatureValueElement(byte[] bytes) {
645645
}
646646

647647
String base64codedValue = XMLUtils.encodeToString(bytes);
648-
649-
if (base64codedValue.length() > 76 && !XMLUtils.ignoreLineBreaks()) {
650-
base64codedValue = "\n" + base64codedValue + "\n";
651-
}
652-
653648
Text t = createText(base64codedValue);
654649
signatureValueElement.appendChild(t);
655650
}

src/main/java/org/apache/xml/security/stax/ext/AbstractOutputProcessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.xml.security.stax.ext.stax.XMLSecEventFactory;
3838
import org.apache.xml.security.stax.ext.stax.XMLSecNamespace;
3939
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
40+
import org.apache.xml.security.utils.XMLUtils;
4041
import org.w3c.dom.Attr;
4142
import org.w3c.dom.Element;
4243
import org.w3c.dom.NamedNodeMap;
@@ -72,6 +73,7 @@ public void setAction(XMLSecurityConstants.Action action) {
7273

7374
@Override
7475
public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityException {
76+
XMLUtils.setThreadLocalBase64Parameters(securityProperties.getBase64LineLength(), securityProperties.getBase64LineSeparator());
7577
outputProcessorChain.addProcessor(this);
7678
}
7779

src/main/java/org/apache/xml/security/stax/ext/XMLSecurityProperties.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.apache.xml.security.stax.ext;
2020

2121
import org.apache.xml.security.stax.securityToken.SecurityTokenConstants;
22+
import org.apache.xml.security.utils.XMLUtils;
2223

2324
import java.security.Key;
2425
import java.security.cert.X509Certificate;
@@ -78,6 +79,8 @@ public class XMLSecurityProperties {
7879

7980
private QName signaturePositionQName;
8081
private boolean signaturePositionStart = false;
82+
private int base64LineLength = XMLUtils.DEFAULT_BASE64_LINE_LENGTH;
83+
private byte[] base64LineSeparator = XMLUtils.DEFAULT_BASE64_LINE_SEPARATOR;
8184

8285
public XMLSecurityProperties() {
8386
}
@@ -117,6 +120,8 @@ protected XMLSecurityProperties(XMLSecurityProperties xmlSecurityProperties) {
117120
this.signatureIncludeDigestTransform = xmlSecurityProperties.signatureIncludeDigestTransform;
118121
this.signaturePositionQName = xmlSecurityProperties.signaturePositionQName;
119122
this.signaturePositionStart = xmlSecurityProperties.signaturePositionStart;
123+
this.base64LineSeparator = xmlSecurityProperties.base64LineSeparator;
124+
this.base64LineLength = xmlSecurityProperties.base64LineLength;
120125
}
121126

122127
public boolean isSignaturePositionStart() {
@@ -528,4 +533,44 @@ public QName getSignaturePositionQName() {
528533
public void setSignaturePositionQName(QName signaturePositionQName) {
529534
this.signaturePositionQName = signaturePositionQName;
530535
}
536+
537+
/**
538+
* @return the Base64 line separator, or {@code null} for no line separator.
539+
*/
540+
public byte[] getBase64LineSeparator() {
541+
return base64LineSeparator;
542+
}
543+
544+
/**
545+
* Sets the Base64 line separator to the given US-ASCII bytes.
546+
* The default is CRLF.
547+
* For no line separator, use the combination of {@code base64LineLength = 4} and
548+
* {@code base64LineSeparator = null}.
549+
*
550+
* @param base64LineSeparator a Base64 line separator, or {@code null} for no line separator.
551+
* @see #setBase64LineLength(int)
552+
*/
553+
public void setBase64LineSeparator(byte[] base64LineSeparator) {
554+
this.base64LineSeparator = base64LineSeparator;
555+
}
556+
557+
/**
558+
* @return the Base64 line length, possibly ≤ {@code 0} for no line separator.
559+
*/
560+
public int getBase64LineLength() {
561+
return base64LineLength;
562+
}
563+
564+
/**
565+
* Sets the Base64 line length, which must be a multiple of 4.
566+
* The default is 76.
567+
* For no line separator, use the combination of {@code base64LineLength = 4} and
568+
* {@code base64LineSeparator = null}.
569+
*
570+
* @param base64LineLength a Base64 line length, or {@code 0} for no line separator.
571+
* @see #setBase64LineSeparator(byte[])
572+
*/
573+
public void setBase64LineLength(int base64LineLength) {
574+
this.base64LineLength = base64LineLength;
575+
}
531576
}

src/main/java/org/apache/xml/security/stax/impl/processor/output/AbstractEncryptOutputProcessor.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,7 @@ public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityEx
174174
symmetricCipher.init(Cipher.ENCRYPT_MODE, encryptionPartDef.getSymmetricKey(), parameterSpec);
175175

176176
characterEventGeneratorOutputStream = new CharacterEventGeneratorOutputStream();
177-
Base64OutputStream base64EncoderStream = null;
178-
if (XMLUtils.isIgnoreLineBreaks()) {
179-
base64EncoderStream = new Base64OutputStream(characterEventGeneratorOutputStream, true, 0, null);
180-
} else {
181-
base64EncoderStream = new Base64OutputStream(characterEventGeneratorOutputStream, true);
182-
}
177+
Base64OutputStream base64EncoderStream = XMLUtils.createBase64OutputStream(characterEventGeneratorOutputStream, true);
183178
base64EncoderStream.write(iv);
184179

185180
OutputStream outputStream = new CipherOutputStream(base64EncoderStream, symmetricCipher);

src/main/java/org/apache/xml/security/utils/ElementProxy.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,6 @@ public void addBase64Element(byte[] bytes, String localname) {
292292
el.appendChild(text);
293293

294294
appendSelf(el);
295-
if (!XMLUtils.ignoreLineBreaks()) {
296-
appendSelf(createText("\n"));
297-
}
298295
}
299296
}
300297

@@ -320,9 +317,7 @@ public void addTextElement(String text, String localname) {
320317
*/
321318
public void addBase64Text(byte[] bytes) {
322319
if (bytes != null) {
323-
Text t = XMLUtils.ignoreLineBreaks()
324-
? createText(XMLUtils.encodeToString(bytes))
325-
: createText("\n" + XMLUtils.encodeToString(bytes) + "\n");
320+
Text t = createText(XMLUtils.encodeToString(bytes));
326321
appendSelf(t);
327322
}
328323
}

src/main/java/org/apache/xml/security/utils/XMLUtils.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@
4040
import javax.xml.parsers.DocumentBuilderFactory;
4141
import javax.xml.parsers.ParserConfigurationException;
4242

43+
import org.apache.commons.codec.binary.Base64OutputStream;
4344
import org.apache.xml.security.c14n.CanonicalizationException;
4445
import org.apache.xml.security.c14n.Canonicalizer;
4546
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
47+
import org.apache.xml.security.stax.impl.util.KeyValue;
4648
import org.w3c.dom.Attr;
4749
import org.w3c.dom.Document;
4850
import org.w3c.dom.Element;
@@ -80,6 +82,12 @@ public final class XMLUtils {
8082
private static final Map<ClassLoader, Queue<DocumentBuilder>> DOCUMENT_BUILDERS_DISALLOW_DOCTYPE =
8183
Collections.synchronizedMap(new WeakHashMap<ClassLoader, Queue<DocumentBuilder>>());
8284

85+
public static final int DEFAULT_BASE64_LINE_LENGTH = 76;
86+
public static final byte[] DEFAULT_BASE64_LINE_SEPARATOR = {13, 10};
87+
private static final KeyValue<Integer, byte[]> DEFAULT_BASE64_PARAMETERS = new KeyValue<>(DEFAULT_BASE64_LINE_LENGTH, DEFAULT_BASE64_LINE_SEPARATOR);
88+
private static final KeyValue<Integer, byte[]> IGNORE_LINE_BREAKS_BASE64_PARAMETERS = new KeyValue<>(0, null);
89+
private static final ThreadLocal<KeyValue<Integer, byte[]>> BASE64_PARAMETERS = ThreadLocal.withInitial(() -> DEFAULT_BASE64_PARAMETERS);
90+
8391
/**
8492
* Constructor XMLUtils
8593
*
@@ -528,10 +536,20 @@ public static void addReturnBeforeChild(Element e, Node child) {
528536
}
529537

530538
public static String encodeToString(byte[] bytes) {
531-
if (ignoreLineBreaks) {
539+
KeyValue<Integer, byte[]> base64Parameters = getEffectiveBase64Parameters();
540+
int lineLength = base64Parameters.getKey();
541+
byte[] lineSeparator = base64Parameters.getValue();
542+
if (lineSeparator == null) {
532543
return Base64.getEncoder().encodeToString(bytes);
533544
}
534-
return Base64.getMimeEncoder().encodeToString(bytes);
545+
return Base64.getMimeEncoder(lineLength, lineSeparator).encodeToString(bytes);
546+
}
547+
548+
private static KeyValue<Integer, byte[]> getEffectiveBase64Parameters() {
549+
if (ignoreLineBreaks) {
550+
return IGNORE_LINE_BREAKS_BASE64_PARAMETERS;
551+
}
552+
return BASE64_PARAMETERS.get();
535553
}
536554

537555
public static byte[] decode(String encodedString) {
@@ -542,10 +560,24 @@ public static byte[] decode(byte[] encodedBytes) {
542560
return Base64.getMimeDecoder().decode(encodedBytes);
543561
}
544562

563+
@Deprecated
545564
public static boolean isIgnoreLineBreaks() {
546565
return ignoreLineBreaks;
547566
}
548567

568+
/**
569+
* Creates a new Base64 output stream around a given output stream, using the thread-local Base64 parameters
570+
* which were configured on this thread using {@link #setThreadLocalBase64Parameters(int, byte[])}.
571+
*
572+
* @param out The delegate output stream, which must not be {@code null}.
573+
* @param doEncode {@code true} to encode, {@code false} to decode Base64.
574+
* @return A new Base64 output stream, never {@code null}.
575+
*/
576+
public static Base64OutputStream createBase64OutputStream(OutputStream out, boolean doEncode) {
577+
KeyValue<Integer, byte[]> base64Parameters = getEffectiveBase64Parameters();
578+
return new Base64OutputStream(out, true, base64Parameters.getKey(), base64Parameters.getValue());
579+
}
580+
549581
/**
550582
* Method convertNodelistToSet
551583
*
@@ -857,6 +889,7 @@ public static boolean isDescendantOrSelf(Node ctx, Node descendantOrSelf) {
857889
}
858890
}
859891

892+
@Deprecated
860893
public static boolean ignoreLineBreaks() {
861894
return ignoreLineBreaks;
862895
}
@@ -1125,4 +1158,16 @@ public ClassLoader run() {
11251158
return clazz.getClassLoader();
11261159
}
11271160

1161+
/**
1162+
* Sets the thread-local Base64 parameters that will be used by this thread in further calls to
1163+
* {@link #encodeToString(byte[])} and {@link #createBase64OutputStream(OutputStream, boolean)}.
1164+
* Any thread-local parameters set here will be overruled (have no effect) when the system property
1165+
* {@code "org.apache.xml.security.ignoreLineBreaks"} is set.
1166+
*
1167+
* @param lineLength a line length, 76 by default. Use 0 for no line separator.
1168+
* @param lineSeparator a line separator, CRLF {@code {13, 10}} by default. Use {@code null} for no line separator.
1169+
*/
1170+
public static void setThreadLocalBase64Parameters(int lineLength, byte[] lineSeparator) {
1171+
BASE64_PARAMETERS.set(new KeyValue<>(lineLength, lineSeparator));
1172+
}
11281173
}

0 commit comments

Comments
 (0)