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

Commit 686a1a5

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

8 files changed

Lines changed: 419 additions & 23 deletions

File tree

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

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

663663
String base64codedValue = XMLUtils.encodeToString(bytes);
664-
665-
if (base64codedValue.length() > 76 && !XMLUtils.ignoreLineBreaks()) {
666-
base64codedValue = "\n" + base64codedValue + "\n";
667-
}
668-
669664
Text t = createText(base64codedValue);
670665
signatureValueElement.appendChild(t);
671666
}

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: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,16 @@
1818
*/
1919
package org.apache.xml.security.stax.ext;
2020

21-
import org.apache.xml.security.stax.securityToken.SecurityTokenConstants;
22-
2321
import java.security.Key;
2422
import java.security.cert.X509Certificate;
2523
import java.security.spec.AlgorithmParameterSpec;
2624
import java.util.*;
2725

2826
import javax.xml.namespace.QName;
2927

28+
import org.apache.xml.security.stax.securityToken.SecurityTokenConstants;
29+
import org.apache.xml.security.utils.XMLUtils;
30+
3031

3132
/**
3233
* Main configuration class to supply keys etc.
@@ -80,6 +81,8 @@ public class XMLSecurityProperties {
8081
private QName signaturePositionQName;
8182
private boolean signaturePositionStart = false;
8283
private AlgorithmParameterSpec algorithmParameterSpec;
84+
private int base64LineLength = XMLUtils.DEFAULT_BASE64_LINE_LENGTH;
85+
private byte[] base64LineSeparator = XMLUtils.DEFAULT_BASE64_LINE_SEPARATOR;
8386

8487
public XMLSecurityProperties() {
8588
}
@@ -120,6 +123,8 @@ protected XMLSecurityProperties(XMLSecurityProperties xmlSecurityProperties) {
120123
this.signaturePositionQName = xmlSecurityProperties.signaturePositionQName;
121124
this.signaturePositionStart = xmlSecurityProperties.signaturePositionStart;
122125
this.algorithmParameterSpec = xmlSecurityProperties.algorithmParameterSpec;
126+
this.base64LineSeparator = xmlSecurityProperties.base64LineSeparator;
127+
this.base64LineLength = xmlSecurityProperties.base64LineLength;
123128
}
124129

125130
public boolean isSignaturePositionStart() {
@@ -539,4 +544,44 @@ public AlgorithmParameterSpec getAlgorithmParameterSpec() {
539544
public void setAlgorithmParameterSpec(AlgorithmParameterSpec algorithmParameterSpec) {
540545
this.algorithmParameterSpec = algorithmParameterSpec;
541546
}
547+
548+
/**
549+
* @return the Base64 line separator, or {@code null} for no line separator.
550+
*/
551+
public byte[] getBase64LineSeparator() {
552+
return base64LineSeparator;
553+
}
554+
555+
/**
556+
* Sets the Base64 line separator to the given US-ASCII bytes.
557+
* The default is CRLF.
558+
* For no line separator, use the combination of {@code base64LineLength = 4} and
559+
* {@code base64LineSeparator = null}.
560+
*
561+
* @param base64LineSeparator a Base64 line separator, or {@code null} for no line separator.
562+
* @see #setBase64LineLength(int)
563+
*/
564+
public void setBase64LineSeparator(byte[] base64LineSeparator) {
565+
this.base64LineSeparator = base64LineSeparator;
566+
}
567+
568+
/**
569+
* @return the Base64 line length, possibly ≤ {@code 0} for no line separator.
570+
*/
571+
public int getBase64LineLength() {
572+
return base64LineLength;
573+
}
574+
575+
/**
576+
* Sets the Base64 line length, which must be a multiple of 4.
577+
* The default is 76.
578+
* For no line separator, use the combination of {@code base64LineLength = 4} and
579+
* {@code base64LineSeparator = null}.
580+
*
581+
* @param base64LineLength a Base64 line length, or {@code 0} for no line separator.
582+
* @see #setBase64LineSeparator(byte[])
583+
*/
584+
public void setBase64LineLength(int base64LineLength) {
585+
this.base64LineLength = base64LineLength;
586+
}
542587
}

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 & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
import java.util.List;
3232
import java.util.Set;
3333

34+
import org.apache.commons.codec.binary.Base64OutputStream;
3435
import org.apache.xml.security.c14n.CanonicalizationException;
3536
import org.apache.xml.security.c14n.Canonicalizer;
3637
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
3738
import org.apache.xml.security.parser.XMLParser;
3839
import org.apache.xml.security.parser.XMLParserException;
3940
import org.apache.xml.security.parser.XMLParserImpl;
41+
import org.apache.xml.security.stax.impl.util.KeyValue;
4042
import org.w3c.dom.Attr;
4143
import org.w3c.dom.Document;
4244
import org.w3c.dom.Element;
@@ -77,6 +79,12 @@ public final class XMLUtils {
7779
private static volatile String xencPrefix = "xenc";
7880
private static volatile String xenc11Prefix = "xenc11";
7981

82+
public static final int DEFAULT_BASE64_LINE_LENGTH = 76;
83+
public static final byte[] DEFAULT_BASE64_LINE_SEPARATOR = {13, 10};
84+
private static final KeyValue<Integer, byte[]> DEFAULT_BASE64_PARAMETERS = new KeyValue<>(DEFAULT_BASE64_LINE_LENGTH, DEFAULT_BASE64_LINE_SEPARATOR);
85+
private static final KeyValue<Integer, byte[]> IGNORE_LINE_BREAKS_BASE64_PARAMETERS = new KeyValue<>(0, null);
86+
private static final ThreadLocal<KeyValue<Integer, byte[]>> BASE64_PARAMETERS = ThreadLocal.withInitial(() -> DEFAULT_BASE64_PARAMETERS);
87+
8088
/**
8189
* Constructor XMLUtils
8290
*
@@ -525,10 +533,20 @@ public static void addReturnBeforeChild(Element e, Node child) {
525533
}
526534

527535
public static String encodeToString(byte[] bytes) {
528-
if (ignoreLineBreaks) {
536+
KeyValue<Integer, byte[]> base64Parameters = getEffectiveBase64Parameters();
537+
int lineLength = base64Parameters.getKey();
538+
byte[] lineSeparator = base64Parameters.getValue();
539+
if (lineSeparator == null) {
529540
return Base64.getEncoder().encodeToString(bytes);
530541
}
531-
return Base64.getMimeEncoder().encodeToString(bytes);
542+
return Base64.getMimeEncoder(lineLength, lineSeparator).encodeToString(bytes);
543+
}
544+
545+
private static KeyValue<Integer, byte[]> getEffectiveBase64Parameters() {
546+
if (ignoreLineBreaks) {
547+
return IGNORE_LINE_BREAKS_BASE64_PARAMETERS;
548+
}
549+
return BASE64_PARAMETERS.get();
532550
}
533551

534552
public static byte[] decode(String encodedString) {
@@ -539,10 +557,24 @@ public static byte[] decode(byte[] encodedBytes) {
539557
return Base64.getMimeDecoder().decode(encodedBytes);
540558
}
541559

560+
@Deprecated
542561
public static boolean isIgnoreLineBreaks() {
543562
return ignoreLineBreaks;
544563
}
545564

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

889+
@Deprecated
857890
public static boolean ignoreLineBreaks() {
858891
return ignoreLineBreaks;
859892
}
@@ -1050,6 +1083,16 @@ public static byte[] getBytes(BigInteger big, int bitlen) {
10501083
return resizedBytes;
10511084
}
10521085

1053-
1054-
1086+
/**
1087+
* Sets the thread-local Base64 parameters that will be used by this thread in further calls to
1088+
* {@link #encodeToString(byte[])} and {@link #createBase64OutputStream(OutputStream, boolean)}.
1089+
* Any thread-local parameters set here will be overruled (have no effect) when the system property
1090+
* {@code "org.apache.xml.security.ignoreLineBreaks"} is set.
1091+
*
1092+
* @param lineLength a line length, 76 by default. Use 0 for no line separator.
1093+
* @param lineSeparator a line separator, CRLF {@code {13, 10}} by default. Use {@code null} for no line separator.
1094+
*/
1095+
public static void setThreadLocalBase64Parameters(int lineLength, byte[] lineSeparator) {
1096+
BASE64_PARAMETERS.set(new KeyValue<>(lineLength, lineSeparator));
1097+
}
10551098
}

0 commit comments

Comments
 (0)