Skip to content

Commit

Permalink
KEYCLOAK-3971 Explicitly set encoding for SAML message processing
Browse files Browse the repository at this point in the history
  • Loading branch information
hmlnarik committed Dec 15, 2016
1 parent 3c2a12d commit 7d51df4
Show file tree
Hide file tree
Showing 23 changed files with 229 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static void sendSaml(boolean asRequest, HttpFacade httpFacade, String act
httpFacade.getResponse().setHeader("Content-Type", "text/html");
httpFacade.getResponse().setHeader("Pragma", "no-cache");
httpFacade.getResponse().setHeader("Cache-Control", "no-cache, no-store");
httpFacade.getResponse().getOutputStream().write(html.getBytes());
httpFacade.getResponse().getOutputStream().write(html.getBytes(GeneralConstants.SAML_CHARSET));
httpFacade.getResponse().end();
} else {
String uri = asRequest ? binding.redirectBinding(document).requestURI(actionUrl).toString() : binding.redirectBinding(document).responseURI(actionUrl).toString();
Expand Down
30 changes: 27 additions & 3 deletions common/src/main/java/org/keycloak/common/util/StreamUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,49 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public final class StreamUtil {

private static final int BUFFER_LENGTH = 4096;

private StreamUtil() {
}

/**
* Reads string from byte input stream.
* @param in InputStream to build the String from
* @return String representation of the input stream contents decoded using default charset
* @throws IOException
* @deprecated Use {@link #readString(java.io.InputStream, java.nio.charset.Charset)} variant.
*/
@Deprecated
public static String readString(InputStream in) throws IOException
{
char[] buffer = new char[1024];
return readString(in, Charset.defaultCharset());
}

/**
* Reads string from byte input stream.
* @param in InputStream to build the String from
* @param charset Charset used to decode the input stream
* @return String representation of the input stream contents decoded using given charset
* @throws IOException
* @deprecated Use {@link #readString(java.io.InputStream, java.nio.charset.Charset)} variant.
*/
public static String readString(InputStream in, Charset charset) throws IOException
{
char[] buffer = new char[BUFFER_LENGTH];
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
int wasRead;
do
{
wasRead = reader.read(buffer, 0, 1024);
wasRead = reader.read(buffer, 0, BUFFER_LENGTH);
if (wasRead > 0)
{
builder.append(buffer, 0, wasRead);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.keycloak.saml.common.constants;

import java.nio.charset.Charset;

/**
* Constants
Expand Down Expand Up @@ -147,4 +148,7 @@ public interface GeneralConstants {
String BASE64_ENCODE_WSTRUST_SECRET_KEY = "picketlink.wstrust.base64_encode_wstrust_secret_key";

String HTTP_HEADER_X_REQUESTED_WITH = "X-Requested-With";

public static final String SAML_CHARSET_NAME = System.getProperty("keycloak.saml.saml_message_charset", "UTF-8");
public static final Charset SAML_CHARSET = Charset.forName(SAML_CHARSET_NAME);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.keycloak.saml;

import org.jboss.logging.Logger;

import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
Expand All @@ -29,6 +30,7 @@
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Expand All @@ -38,7 +40,6 @@
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.KeyPair;
Expand Down Expand Up @@ -155,8 +156,8 @@ public BasePostBindingBuilder(BaseSAML2BindingBuilder builder, Document document
}

public String encoded() throws ProcessingException, ConfigurationException, IOException {
byte[] responseBytes = DocumentUtil.getDocumentAsString(document).getBytes("UTF-8");
return PostBindingUtil.base64Encode(new String(responseBytes));
byte[] responseBytes = DocumentUtil.getDocumentAsString(document).getBytes(GeneralConstants.SAML_CHARSET);
return PostBindingUtil.base64Encode(new String(responseBytes, GeneralConstants.SAML_CHARSET));
}
public Document getDocument() {
return document;
Expand Down Expand Up @@ -300,8 +301,8 @@ public void signAssertion(Document samlDocument) throws ProcessingException {


public String buildHtmlPostResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
byte[] responseBytes = org.keycloak.saml.common.util.DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
byte[] responseBytes = org.keycloak.saml.common.util.DocumentUtil.getDocumentAsString(responseDoc).getBytes(GeneralConstants.SAML_CHARSET);
String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes, GeneralConstants.SAML_CHARSET));

return buildHtml(samlResponse, actionUrl, asRequest);
}
Expand All @@ -315,34 +316,34 @@ public String buildHtml(String samlResponse, String actionUrl, boolean asRequest
key = GeneralConstants.SAML_REQUEST_KEY;
}

builder.append("<HTML>");
builder.append("<HEAD>");
builder.append("<HTML>")
.append("<HEAD>")

builder.append("<TITLE>SAML HTTP Post Binding</TITLE>");
builder.append("</HEAD>");
builder.append("<BODY Onload=\"document.forms[0].submit()\">");
.append("<TITLE>SAML HTTP Post Binding</TITLE>")
.append("</HEAD>")
.append("<BODY Onload=\"document.forms[0].submit()\">")

builder.append("<FORM METHOD=\"POST\" ACTION=\"" + actionUrl + "\">");
builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"" + key + "\"" + " VALUE=\"" + samlResponse + "\"/>");
.append("<FORM METHOD=\"POST\" ACTION=\"").append(actionUrl).append("\">")
.append("<INPUT TYPE=\"HIDDEN\" NAME=\"").append(key).append("\"").append(" VALUE=\"").append(samlResponse).append("\"/>");

if (isNotNull(relayState)) {
builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"RelayState\" " + "VALUE=\"" + escapeAttribute(relayState) + "\"/>");
builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"RelayState\" " + "VALUE=\"").append(escapeAttribute(relayState)).append("\"/>");
}

builder.append("<NOSCRIPT>");
builder.append("<P>JavaScript is disabled. We strongly recommend to enable it. Click the button below to continue.</P>");
builder.append("<INPUT TYPE=\"SUBMIT\" VALUE=\"CONTINUE\" />");
builder.append("</NOSCRIPT>");
builder.append("<NOSCRIPT>")
.append("<P>JavaScript is disabled. We strongly recommend to enable it. Click the button below to continue.</P>")
.append("<INPUT TYPE=\"SUBMIT\" VALUE=\"CONTINUE\" />")
.append("</NOSCRIPT>")

builder.append("</FORM></BODY></HTML>");
.append("</FORM></BODY></HTML>");

return builder.toString();
}

public String base64Encoded(Document document) throws ConfigurationException, ProcessingException, IOException {
String documentAsString = DocumentUtil.getDocumentAsString(document);
logger.debugv("saml document: {0}", documentAsString);
byte[] responseBytes = documentAsString.getBytes("UTF-8");
byte[] responseBytes = documentAsString.getBytes(GeneralConstants.SAML_CHARSET);

return RedirectBindingUtil.deflateBase64URLEncode(responseBytes);
}
Expand All @@ -364,9 +365,9 @@ public URI generateRedirectUri(String samlParameterName, String redirectUri, Doc
byte[] sig = new byte[0];
try {
signature.initSign(signingKeyPair.getPrivate());
signature.update(rawQuery.getBytes("UTF-8"));
signature.update(rawQuery.getBytes(GeneralConstants.SAML_CHARSET));
sig = signature.sign();
} catch (InvalidKeyException | UnsupportedEncodingException | SignatureException e) {
} catch (InvalidKeyException | SignatureException e) {
throw new ProcessingException(e);
}
String encodedSig = RedirectBindingUtil.base64URLEncode(sig);
Expand Down
14 changes: 8 additions & 6 deletions saml-core/src/main/java/org/keycloak/saml/SAMLRequestParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
package org.keycloak.saml;

import org.jboss.logging.Logger;

import org.keycloak.common.util.StreamUtil;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
Expand All @@ -45,13 +47,13 @@ public static SAMLDocumentHolder parseRequestRedirectBinding(String samlMessage)
if (log.isDebugEnabled()) {
String message = null;
try {
message = StreamUtil.readString(is);
message = StreamUtil.readString(is, GeneralConstants.SAML_CHARSET);
} catch (IOException e) {
throw new RuntimeException(e);
}
log.debug("SAML Redirect Binding");
log.debug(message);
is = new ByteArrayInputStream(message.getBytes());
is = new ByteArrayInputStream(message.getBytes(GeneralConstants.SAML_CHARSET));

}
SAML2Request saml2Request = new SAML2Request();
Expand All @@ -69,7 +71,7 @@ public static SAMLDocumentHolder parseRequestPostBinding(String samlMessage) {
InputStream is;
byte[] samlBytes = PostBindingUtil.base64Decode(samlMessage);
if (log.isDebugEnabled()) {
String str = new String(samlBytes);
String str = new String(samlBytes, GeneralConstants.SAML_CHARSET);
log.debug("SAML POST Binding");
log.debug(str);
}
Expand All @@ -92,7 +94,7 @@ public static SAMLDocumentHolder parseResponsePostBinding(String samlMessage) {

public static SAMLDocumentHolder parseResponseDocument(byte[] samlBytes) {
if (log.isDebugEnabled()) {
String str = new String(samlBytes);
String str = new String(samlBytes, GeneralConstants.SAML_CHARSET);
log.debug(str);
}
InputStream is = new ByteArrayInputStream(samlBytes);
Expand All @@ -111,13 +113,13 @@ public static SAMLDocumentHolder parseResponseRedirectBinding(String samlMessage
if (log.isDebugEnabled()) {
String message = null;
try {
message = StreamUtil.readString(is);
message = StreamUtil.readString(is, GeneralConstants.SAML_CHARSET);
} catch (IOException e) {
throw new RuntimeException(e);
}
log.debug("SAML Redirect Binding");
log.debug(message);
is = new ByteArrayInputStream(message.getBytes());
is = new ByteArrayInputStream(message.getBytes(GeneralConstants.SAML_CHARSET));

}
SAML2Response response = new SAML2Response();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ public static String getNodeAsString(Node node) throws ConfigurationException, P
throw logger.processingError(e);
}

return new String(baos.toByteArray());
return new String(baos.toByteArray(), GeneralConstants.SAML_CHARSET);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static void flush(XMLStreamWriter writer) throws ProcessingException {
public static XMLEventWriter getXMLEventWriter(final OutputStream outStream) throws ProcessingException {
XMLOutputFactory xmlOutputFactory = getXMLOutputFactory();
try {
return xmlOutputFactory.createXMLEventWriter(outStream, "UTF-8");
return xmlOutputFactory.createXMLEventWriter(outStream, GeneralConstants.SAML_CHARSET_NAME);
} catch (XMLStreamException e) {
throw logger.processingError(e);
}
Expand All @@ -93,7 +93,7 @@ public static XMLEventWriter getXMLEventWriter(final OutputStream outStream) thr
public static XMLStreamWriter getXMLStreamWriter(final OutputStream outStream) throws ProcessingException {
XMLOutputFactory xmlOutputFactory = getXMLOutputFactory();
try {
return xmlOutputFactory.createXMLStreamWriter(outStream, "UTF-8");
return xmlOutputFactory.createXMLStreamWriter(outStream, GeneralConstants.SAML_CHARSET_NAME);
} catch (XMLStreamException e) {
throw logger.processingError(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
Expand All @@ -39,6 +40,7 @@
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLRequestWriter;
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLResponseWriter;
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;

import org.w3c.dom.Document;

import javax.xml.datatype.XMLGregorianCalendar;
Expand Down Expand Up @@ -274,7 +276,7 @@ public Document convert(RequestAbstractType rat) throws ProcessingException, Con
writer.write((LogoutRequestType) rat);
}

return DocumentUtil.getDocument(new String(bos.toByteArray()));
return DocumentUtil.getDocument(new String(bos.toByteArray(), GeneralConstants.SAML_CHARSET));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.keycloak.saml.processing.api.util;

import org.keycloak.saml.common.constants.GeneralConstants;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
Expand Down Expand Up @@ -62,7 +64,7 @@ public static byte[] encode(byte[] message) throws IOException {
* @throws IOException
*/
public static byte[] encode(String message) throws IOException {
return encode(message.getBytes());
return encode(message.getBytes(GeneralConstants.SAML_CHARSET));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
import org.keycloak.dom.xmlsec.w3.xmlenc.EncryptionMethodType;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.ParserNamespaceSupport;
import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil;
import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;

import org.w3c.dom.Element;

import javax.xml.namespace.QName;
Expand Down Expand Up @@ -476,7 +478,7 @@ private EncryptionMethodType parseEncryptionMethod(XMLEventReader xmlEventReader
keySize = BigInteger.valueOf(Long.valueOf(StaxParserUtil.getElementText(xmlEventReader)));
} else if ("OAEPparams".equals(localPart)) {
startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
OAEPparams = StaxParserUtil.getElementText(xmlEventReader).getBytes();
OAEPparams = StaxParserUtil.getElementText(xmlEventReader).getBytes(GeneralConstants.SAML_CHARSET);
} else {
throw logger.parserUnknownTag(localPart, startElement.getLocation());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.keycloak.dom.xmlsec.w3.xmldsig.X509DataType;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.constants.WSTrustConstants;
Expand All @@ -50,6 +51,7 @@
import org.keycloak.saml.processing.core.saml.v1.SAML11Constants;
import org.keycloak.saml.processing.core.saml.v2.util.SignatureUtil;
import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;

import org.w3c.dom.Element;

import javax.xml.namespace.QName;
Expand Down Expand Up @@ -561,7 +563,7 @@ public static KeyInfoType parseKeyInfo(XMLEventReader xmlEventReader) throws Par

X509CertificateType cert = new X509CertificateType();
String certValue = StaxParserUtil.getElementText(xmlEventReader);
cert.setEncodedCertificate(certValue.getBytes());
cert.setEncodedCertificate(certValue.getBytes(GeneralConstants.SAML_CHARSET));
x509.add(cert);

EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader);
Expand Down Expand Up @@ -614,11 +616,11 @@ public static RSAKeyValueType parseRSAKeyValue(XMLEventReader xmlEventReader) th
if (tag.equals(WSTrustConstants.XMLDSig.MODULUS)) {
startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
String text = StaxParserUtil.getElementText(xmlEventReader);
rsaKeyValue.setModulus(text.getBytes());
rsaKeyValue.setModulus(text.getBytes(GeneralConstants.SAML_CHARSET));
} else if (tag.equals(WSTrustConstants.XMLDSig.EXPONENT)) {
startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
String text = StaxParserUtil.getElementText(xmlEventReader);
rsaKeyValue.setExponent(text.getBytes());
rsaKeyValue.setExponent(text.getBytes(GeneralConstants.SAML_CHARSET));
} else
throw logger.parserUnknownTag(tag, startElement.getLocation());
}
Expand Down
Loading

0 comments on commit 7d51df4

Please sign in to comment.