Skip to content

Commit 02180c4

Browse files
committed
Added properties for base64 formatting
1 parent 32a81f0 commit 02180c4

5 files changed

Lines changed: 407 additions & 23 deletions

File tree

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -684,11 +684,7 @@ private void setSignatureValueElement(byte[] bytes) {
684684
signatureValueElement.removeChild(signatureValueElement.getFirstChild());
685685
}
686686

687-
String base64codedValue = XMLUtils.encodeToString(bytes);
688-
689-
if (base64codedValue.length() > 76 && !XMLUtils.ignoreLineBreaks()) {
690-
base64codedValue = "\n" + base64codedValue + "\n";
691-
}
687+
String base64codedValue = XMLUtils.encodeElementValue(bytes);
692688

693689
Text t = createText(base64codedValue);
694690
signatureValueElement.appendChild(t);

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
import javax.xml.stream.XMLStreamConstants;
4141
import javax.xml.stream.XMLStreamException;
4242

43-
import org.apache.commons.codec.binary.Base64;
44-
import org.apache.commons.codec.binary.Base64OutputStream;
4543
import org.apache.xml.security.algorithms.JCEMapper;
4644
import org.apache.xml.security.encryption.XMLCipherUtil;
4745
import org.apache.xml.security.exceptions.XMLSecurityException;
@@ -176,12 +174,7 @@ public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityEx
176174
symmetricCipher.init(Cipher.ENCRYPT_MODE, encryptionPartDef.getSymmetricKey(), parameterSpec);
177175

178176
characterEventGeneratorOutputStream = new CharacterEventGeneratorOutputStream();
179-
Base64OutputStream.Builder builder = Base64OutputStream.builder()
180-
.setOutputStream(characterEventGeneratorOutputStream)
181-
.setEncode(true);
182-
if (XMLUtils.isIgnoreLineBreaks()) {
183-
builder.setBaseNCodec(Base64.builder().setLineLength(0).setLineSeparator(null).get());
184-
}
177+
OutputStream base64EncoderStream = XMLUtils.encodeStream(characterEventGeneratorOutputStream); //NOPMD
185178

186179
Base64OutputStream base64EncoderStream = builder.get(); //NOPMD
187180
base64EncoderStream.write(iv);

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,7 @@ public void addTextElement(String text, String localname) {
313313
*/
314314
public void addBase64Text(byte[] bytes) {
315315
if (bytes != null) {
316-
Text t = XMLUtils.ignoreLineBreaks()
317-
? createText(XMLUtils.encodeToString(bytes))
318-
: createText("\n" + XMLUtils.encodeToString(bytes) + "\n");
316+
Text t = createText(XMLUtils.encodeElementValue(bytes));
319317
appendSelf(t);
320318
}
321319
}

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

Lines changed: 134 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,63 @@
5656
/**
5757
* DOM and XML accessibility and comfort functions.
5858
*
59+
* @implNote
60+
* Following system properties affect XML formatting:
61+
* <ul>
62+
* <li>{@systemProperty org.apache.xml.security.ignoreLineBreaks} - ignores all line breaks,
63+
* making a single-line document. Overrides all other formatting options. Default: false</li>
64+
* <li>{@systemProperty org.apache.xml.security.base64.ignoreLineBreaks} - ignores line breaks in base64Binary values.
65+
* Takes precedence over line length and separator options (see below). Default: false</li>
66+
* <li>{@systemProperty org.apache.xml.security.base64.lineSeparator} - Sets the line separator sequence in base64Binary values.
67+
* Possible values: crlf, lf. Default: crlf</li>
68+
* <li>{@systemProperty org.apache.xml.security.base64.lineLength} - Sets maximum line length in base64Binary values.
69+
* The value is rounded down to nearest multiple of 4. Values less than 4 are ignored. Default: 76</li>
70+
* </ul>
5971
*/
6072
public final class XMLUtils {
6173

74+
private static final Logger LOG = System.getLogger(XMLUtils.class.getName());
75+
76+
private static final String IGNORE_LINE_BREAKS_PROP = "org.apache.xml.security.ignoreLineBreaks";
77+
private static final String BASE64_IGNORE_LINE_BREAKS_PROP = "org.apache.xml.security.base64.ignoreLineBreaks";
78+
private static final String BASE64_LINE_SEPARATOR_PROP = "org.apache.xml.security.base64.lineSeparator";
79+
private static final String BASE64_LINE_LENGTH_PROP = "org.apache.xml.security.base64.lineLength";
80+
6281
private static boolean ignoreLineBreaks =
6382
AccessController.doPrivileged(
64-
(PrivilegedAction<Boolean>) () -> Boolean.getBoolean("org.apache.xml.security.ignoreLineBreaks"));
83+
(PrivilegedAction<Boolean>) () -> Boolean.getBoolean(IGNORE_LINE_BREAKS_PROP));
84+
85+
private static Base64FormattingOptions base64Formatting =
86+
AccessController.doPrivileged((PrivilegedAction<Base64FormattingOptions>) () -> {
87+
Base64FormattingOptions options = new Base64FormattingOptions();
88+
options.setIgnoreLineBreaks(Boolean.getBoolean(BASE64_IGNORE_LINE_BREAKS_PROP));
89+
90+
String lineSeparator = System.getProperty(BASE64_LINE_SEPARATOR_PROP);
91+
if (lineSeparator != null) {
92+
try {
93+
options.setLineSeparator(Base64LineSeparator.valueOf(lineSeparator.toUpperCase()));
94+
} catch (IllegalArgumentException e) {
95+
LOG.log(Level.WARNING, "Illegal value of {0} property ignored: {1}",
96+
BASE64_LINE_SEPARATOR_PROP, lineSeparator);
97+
}
98+
}
6599

66-
private static final Logger LOG = System.getLogger(XMLUtils.class.getName());
100+
Integer lineLength = Integer.getInteger(BASE64_LINE_LENGTH_PROP);
101+
if (lineLength != null && lineLength >= 4) {
102+
options.setLineLength(lineLength);
103+
} else if (lineLength != null) {
104+
LOG.log(Level.WARNING, "Illegal value of {0} property ignored: {1}",
105+
BASE64_LINE_LENGTH_PROP, lineLength);
106+
}
107+
108+
return options;
109+
});
110+
111+
private static Base64.Encoder base64Encoder = (ignoreLineBreaks || base64Formatting.isIgnoreLineBreaks()) ?
112+
Base64.getEncoder() :
113+
Base64.getMimeEncoder(base64Formatting.getLineLength(), base64Formatting.getLineSeparator().getBytes());
114+
115+
private static Base64.Decoder base64Decoder = Base64.getMimeDecoder();
67116

68117
private static XMLParser xmlParserImpl =
69118
AccessController.doPrivileged(
@@ -515,18 +564,48 @@ public static void addReturnBeforeChild(Element e, Node child) {
515564
}
516565

517566
public static String encodeToString(byte[] bytes) {
518-
if (ignoreLineBreaks) {
519-
return Base64.getEncoder().encodeToString(bytes);
567+
return base64Encoder.encodeToString(bytes);
568+
}
569+
570+
/**
571+
* Encodes bytes using Base64, with or without line breaks, depending on configuration (see {@link XMLUtils}).
572+
* @param bytes Bytes to encode
573+
* @return Base64 string
574+
*/
575+
public static String encodeElementValue(byte[] bytes) {
576+
String encoded = encodeToString(bytes);
577+
if (!ignoreLineBreaks && !base64Formatting.isIgnoreLineBreaks()
578+
&& encoded.length() > base64Formatting.getLineLength()) {
579+
encoded = "\n" + encoded + "\n";
520580
}
521-
return Base64.getMimeEncoder().encodeToString(bytes);
581+
return encoded;
582+
}
583+
584+
/**
585+
* Wraps output stream for Base64 encoding.
586+
* Output data may contain line breaks or not, depending on configuration (see {@link XMLUtils})
587+
* @param stream The underlying output stream to write Base64-encoded data
588+
* @return Stream which writes binary data using Base64 encoder
589+
*/
590+
public static OutputStream encodeStream(OutputStream stream) {
591+
return base64Encoder.wrap(stream);
522592
}
523593

524594
public static byte[] decode(String encodedString) {
525-
return Base64.getMimeDecoder().decode(encodedString);
595+
return base64Decoder.decode(encodedString);
526596
}
527597

528598
public static byte[] decode(byte[] encodedBytes) {
529-
return Base64.getMimeDecoder().decode(encodedBytes);
599+
return base64Decoder.decode(encodedBytes);
600+
}
601+
602+
/**
603+
* Wraps input stream for Base64 decoding.
604+
* @param stream Input stream with Base64-encoded data
605+
* @return Input stream with decoded binary data
606+
*/
607+
public static InputStream decodeStream(InputStream stream) {
608+
return base64Decoder.wrap(stream);
530609
}
531610

532611
public static boolean isIgnoreLineBreaks() {
@@ -1068,4 +1147,52 @@ public static byte[] getBytes(BigInteger big, int bitlen) {
10681147

10691148
return resizedBytes;
10701149
}
1150+
1151+
/**
1152+
* Aggregates formatting options for base64Binary values.
1153+
*/
1154+
static class Base64FormattingOptions {
1155+
private boolean ignoreLineBreaks = false;
1156+
private Base64LineSeparator lineSeparator = Base64LineSeparator.CRLF;
1157+
private int lineLength = 76;
1158+
1159+
public boolean isIgnoreLineBreaks() {
1160+
return ignoreLineBreaks;
1161+
}
1162+
1163+
public void setIgnoreLineBreaks(boolean ignoreLineBreaks) {
1164+
this.ignoreLineBreaks = ignoreLineBreaks;
1165+
}
1166+
1167+
public Base64LineSeparator getLineSeparator() {
1168+
return lineSeparator;
1169+
}
1170+
1171+
public void setLineSeparator(Base64LineSeparator lineSeparator) {
1172+
this.lineSeparator = lineSeparator;
1173+
}
1174+
1175+
public int getLineLength() {
1176+
return lineLength;
1177+
}
1178+
1179+
public void setLineLength(int lineLength) {
1180+
this.lineLength = lineLength;
1181+
}
1182+
}
1183+
1184+
enum Base64LineSeparator {
1185+
CRLF(new byte[]{'\r', '\n'}),
1186+
LF(new byte[]{'\n'});
1187+
1188+
private byte[] bytes;
1189+
1190+
Base64LineSeparator(byte[] bytes) {
1191+
this.bytes = bytes;
1192+
}
1193+
1194+
public byte[] getBytes() {
1195+
return bytes;
1196+
}
1197+
}
10711198
}

0 commit comments

Comments
 (0)