Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -684,11 +684,7 @@ private void setSignatureValueElement(byte[] bytes) {
signatureValueElement.removeChild(signatureValueElement.getFirstChild());
}

String base64codedValue = XMLUtils.encodeToString(bytes);

if (base64codedValue.length() > 76 && !XMLUtils.ignoreLineBreaks()) {
base64codedValue = "\n" + base64codedValue + "\n";
}
String base64codedValue = XMLUtils.encodeElementValue(bytes);

Text t = createText(base64codedValue);
signatureValueElement.appendChild(t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,7 @@
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;

import javax.crypto.Cipher;
Expand All @@ -40,7 +35,6 @@
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

import org.apache.commons.codec.binary.Base64OutputStream;
import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.encryption.XMLCipherUtil;
import org.apache.xml.security.exceptions.XMLSecurityException;
Expand Down Expand Up @@ -175,12 +169,7 @@ public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityEx
symmetricCipher.init(Cipher.ENCRYPT_MODE, encryptionPartDef.getSymmetricKey(), parameterSpec);

characterEventGeneratorOutputStream = new CharacterEventGeneratorOutputStream();
Base64OutputStream base64EncoderStream = null; //NOPMD
if (XMLUtils.isIgnoreLineBreaks()) {
base64EncoderStream = new Base64OutputStream(characterEventGeneratorOutputStream, true, 0, null);
} else {
base64EncoderStream = new Base64OutputStream(characterEventGeneratorOutputStream, true);
}
OutputStream base64EncoderStream = XMLUtils.encodeStream(characterEventGeneratorOutputStream); //NOPMD
base64EncoderStream.write(iv);

OutputStream outputStream = new CipherOutputStream(base64EncoderStream, symmetricCipher); //NOPMD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,7 @@ public void addTextElement(String text, String localname) {
*/
public void addBase64Text(byte[] bytes) {
if (bytes != null) {
Text t = XMLUtils.ignoreLineBreaks()
? createText(XMLUtils.encodeToString(bytes))
: createText("\n" + XMLUtils.encodeToString(bytes) + "\n");
Text t = createText(XMLUtils.encodeElementValue(bytes));
appendSelf(t);
}
}
Expand Down
141 changes: 134 additions & 7 deletions src/main/java/org/apache/xml/security/utils/XMLUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,63 @@
/**
* DOM and XML accessibility and comfort functions.
*
* @implNote
* Following system properties affect XML formatting:
* <ul>
* <li>{@systemProperty org.apache.xml.security.ignoreLineBreaks} - ignores all line breaks,
* making a single-line document. Overrides all other formatting options. Default: false</li>
* <li>{@systemProperty org.apache.xml.security.base64.ignoreLineBreaks} - ignores line breaks in base64Binary values.
* Takes precedence over line length and separator options (see below). Default: false</li>
* <li>{@systemProperty org.apache.xml.security.base64.lineSeparator} - Sets the line separator sequence in base64Binary values.
* Possible values: crlf, lf. Default: crlf</li>
* <li>{@systemProperty org.apache.xml.security.base64.lineLength} - Sets maximum line length in base64Binary values.
* The value is rounded down to nearest multiple of 4. Values less than 4 are ignored. Default: 76</li>
* </ul>
*/
public final class XMLUtils {

private static final Logger LOG = System.getLogger(XMLUtils.class.getName());

private static final String IGNORE_LINE_BREAKS_PROP = "org.apache.xml.security.ignoreLineBreaks";
private static final String BASE64_IGNORE_LINE_BREAKS_PROP = "org.apache.xml.security.base64.ignoreLineBreaks";
private static final String BASE64_LINE_SEPARATOR_PROP = "org.apache.xml.security.base64.lineSeparator";
private static final String BASE64_LINE_LENGTH_PROP = "org.apache.xml.security.base64.lineLength";

private static boolean ignoreLineBreaks =
AccessController.doPrivileged(
(PrivilegedAction<Boolean>) () -> Boolean.getBoolean("org.apache.xml.security.ignoreLineBreaks"));
(PrivilegedAction<Boolean>) () -> Boolean.getBoolean(IGNORE_LINE_BREAKS_PROP));

private static Base64FormattingOptions base64Formatting =
AccessController.doPrivileged((PrivilegedAction<Base64FormattingOptions>) () -> {
Base64FormattingOptions options = new Base64FormattingOptions();
options.setIgnoreLineBreaks(Boolean.getBoolean(BASE64_IGNORE_LINE_BREAKS_PROP));

String lineSeparator = System.getProperty(BASE64_LINE_SEPARATOR_PROP);
if (lineSeparator != null) {
try {
options.setLineSeparator(Base64LineSeparator.valueOf(lineSeparator.toUpperCase()));
} catch (IllegalArgumentException e) {
LOG.log(Level.WARNING, "Illegal value of {0} property ignored: {1}",
BASE64_LINE_SEPARATOR_PROP, lineSeparator);
}
}

private static final Logger LOG = System.getLogger(XMLUtils.class.getName());
Integer lineLength = Integer.getInteger(BASE64_LINE_LENGTH_PROP);
if (lineLength != null && lineLength >= 4) {
options.setLineLength(lineLength);
} else if (lineLength != null) {
LOG.log(Level.WARNING, "Illegal value of {0} property ignored: {1}",
BASE64_LINE_LENGTH_PROP, lineLength);
}

return options;
});

private static Base64.Encoder base64Encoder = (ignoreLineBreaks || base64Formatting.isIgnoreLineBreaks()) ?
Base64.getEncoder() :
Base64.getMimeEncoder(base64Formatting.getLineLength(), base64Formatting.getLineSeparator().getBytes());

private static Base64.Decoder base64Decoder = Base64.getMimeDecoder();

private static XMLParser xmlParserImpl =
AccessController.doPrivileged(
Expand Down Expand Up @@ -515,18 +564,48 @@ public static void addReturnBeforeChild(Element e, Node child) {
}

public static String encodeToString(byte[] bytes) {
if (ignoreLineBreaks) {
return Base64.getEncoder().encodeToString(bytes);
return base64Encoder.encodeToString(bytes);
}

/**
* Encodes bytes using Base64, with or without line breaks, depending on configuration (see {@link XMLUtils}).
* @param bytes Bytes to encode
* @return Base64 string
*/
public static String encodeElementValue(byte[] bytes) {
String encoded = encodeToString(bytes);
if (!ignoreLineBreaks && !base64Formatting.isIgnoreLineBreaks()
&& encoded.length() > base64Formatting.getLineLength()) {
encoded = "\n" + encoded + "\n";
}
return Base64.getMimeEncoder().encodeToString(bytes);
return encoded;
}

/**
* Wraps output stream for Base64 encoding.
* Output data may contain line breaks or not, depending on configuration (see {@link XMLUtils})
* @param stream The underlying output stream to write Base64-encoded data
* @return Stream which writes binary data using Base64 encoder
*/
public static OutputStream encodeStream(OutputStream stream) {
return base64Encoder.wrap(stream);
}

public static byte[] decode(String encodedString) {
return Base64.getMimeDecoder().decode(encodedString);
return base64Decoder.decode(encodedString);
}

public static byte[] decode(byte[] encodedBytes) {
return Base64.getMimeDecoder().decode(encodedBytes);
return base64Decoder.decode(encodedBytes);
}

/**
* Wraps input stream for Base64 decoding.
* @param stream Input stream with Base64-encoded data
* @return Input stream with decoded binary data
*/
public static InputStream decodeStream(InputStream stream) {
return base64Decoder.wrap(stream);
}

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

return resizedBytes;
}

/**
* Aggregates formatting options for base64Binary values.
*/
static class Base64FormattingOptions {
private boolean ignoreLineBreaks = false;
private Base64LineSeparator lineSeparator = Base64LineSeparator.CRLF;
private int lineLength = 76;

public boolean isIgnoreLineBreaks() {
return ignoreLineBreaks;
}

public void setIgnoreLineBreaks(boolean ignoreLineBreaks) {
this.ignoreLineBreaks = ignoreLineBreaks;
}

public Base64LineSeparator getLineSeparator() {
return lineSeparator;
}

public void setLineSeparator(Base64LineSeparator lineSeparator) {
this.lineSeparator = lineSeparator;
}

public int getLineLength() {
return lineLength;
}

public void setLineLength(int lineLength) {
this.lineLength = lineLength;
}
}

enum Base64LineSeparator {
CRLF(new byte[]{'\r', '\n'}),
LF(new byte[]{'\n'});

private byte[] bytes;

Base64LineSeparator(byte[] bytes) {
this.bytes = bytes;
}

public byte[] getBytes() {
return bytes;
}
}
}
Loading