KEYCLOAK-3971 Explicitly set encoding for SAML message processing

This commit is contained in:
Hynek Mlnarik 2016-12-15 14:01:25 +01:00
parent 3c2a12d019
commit 7d51df4eed
23 changed files with 229 additions and 83 deletions

View file

@ -40,7 +40,7 @@ public class SamlUtil {
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();

View file

@ -21,6 +21,7 @@ import java.io.BufferedReader;
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>
@ -28,18 +29,41 @@ import java.io.InputStreamReader;
*/
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);

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.saml.common.constants;
import java.nio.charset.Charset;
/**
* Constants
@ -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);
}

View file

@ -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;
@ -29,6 +30,7 @@ import org.keycloak.saml.processing.core.saml.v2.util.DocumentUtil;
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;
@ -38,7 +40,6 @@ import javax.crypto.spec.SecretKeySpec;
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;
@ -155,8 +156,8 @@ public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
}
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;
@ -300,8 +301,8 @@ public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
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);
}
@ -315,26 +316,26 @@ public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
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();
}
@ -342,7 +343,7 @@ public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
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);
}
@ -364,9 +365,9 @@ public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
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);

View file

@ -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;
@ -45,13 +47,13 @@ public class SAMLRequestParser {
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();
@ -69,7 +71,7 @@ public class SAMLRequestParser {
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);
}
@ -92,7 +94,7 @@ public class SAMLRequestParser {
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);
@ -111,13 +113,13 @@ public class SAMLRequestParser {
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();

View file

@ -376,7 +376,7 @@ public class DocumentUtil {
throw logger.processingError(e);
}
return new String(baos.toByteArray());
return new String(baos.toByteArray(), GeneralConstants.SAML_CHARSET);
}
/**

View file

@ -75,7 +75,7 @@ public class StaxUtil {
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);
}
@ -93,7 +93,7 @@ public class StaxUtil {
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);
}

View file

@ -25,6 +25,7 @@ import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
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;
@ -39,6 +40,7 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
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;
@ -274,7 +276,7 @@ public class SAML2Request {
writer.write((LogoutRequestType) rat);
}
return DocumentUtil.getDocument(new String(bos.toByteArray()));
return DocumentUtil.getDocument(new String(bos.toByteArray(), GeneralConstants.SAML_CHARSET));
}
/**

View file

@ -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;
@ -62,7 +64,7 @@ public class DeflateUtil {
* @throws IOException
*/
public static byte[] encode(String message) throws IOException {
return encode(message.getBytes());
return encode(message.getBytes(GeneralConstants.SAML_CHARSET));
}
/**

View file

@ -37,6 +37,7 @@ import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
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;
@ -44,6 +45,7 @@ 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;
@ -476,7 +478,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme
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());
}

View file

@ -41,6 +41,7 @@ import org.keycloak.dom.xmlsec.w3.xmldsig.X509CertificateType;
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;
@ -50,6 +51,7 @@ import org.keycloak.saml.processing.core.parsers.saml.SAML11SubjectParser;
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;
@ -561,7 +563,7 @@ public class SAML11ParserUtil {
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);
@ -614,11 +616,11 @@ public class SAML11ParserUtil {
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());
}

View file

@ -34,6 +34,7 @@ import org.keycloak.dom.xmlsec.w3.xmldsig.X509CertificateType;
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;
@ -42,6 +43,7 @@ import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.common.util.StringUtil;
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.datatype.XMLGregorianCalendar;
@ -98,7 +100,7 @@ public class SAMLParserUtil {
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);
@ -151,11 +153,11 @@ public class SAMLParserUtil {
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());
}

View file

@ -49,6 +49,7 @@ import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter;
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@ -62,7 +63,9 @@ import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.keycloak.rotation.HardcodedKeyLocator;
import org.keycloak.saml.common.constants.GeneralConstants;
/**
* Utility to deal with assertions
@ -87,7 +90,7 @@ public class AssertionUtil {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
SAMLAssertionWriter writer = new SAMLAssertionWriter(StaxUtil.getXMLStreamWriter(baos));
writer.write(assertion);
return new String(baos.toByteArray());
return new String(baos.toByteArray(), GeneralConstants.SAML_CHARSET);
}
/**

View file

@ -22,11 +22,13 @@ import org.keycloak.dom.xmlsec.w3.xmldsig.RSAKeyValueType;
import org.keycloak.dom.xmlsec.w3.xmldsig.SignatureType;
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.WSTrustConstants;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.Base64;
import org.keycloak.saml.processing.core.constants.PicketLinkFederationConstants;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@ -106,7 +108,7 @@ public class SignatureUtil {
String algo = signingKey.getAlgorithm();
Signature sig = getSignature(algo);
sig.initSign(signingKey);
sig.update(stringToBeSigned.getBytes());
sig.update(stringToBeSigned.getBytes(GeneralConstants.SAML_CHARSET));
return sig.sign();
}
@ -191,7 +193,7 @@ public class SignatureUtil {
Element childElement = (Element) node;
String tag = childElement.getLocalName();
byte[] text = childElement.getTextContent().getBytes();
byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET);
if (WSTrustConstants.XMLDSig.P.equals(tag)) {
dsa.setP(text);
@ -232,7 +234,7 @@ public class SignatureUtil {
Element childElement = (Element) node;
String tag = childElement.getLocalName();
byte[] text = childElement.getTextContent().getBytes();
byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET);
if (WSTrustConstants.XMLDSig.MODULUS.equals(tag)) {
rsa.setModulus(text);
@ -262,8 +264,8 @@ public class SignatureUtil {
byte[] exponent = pubKey.getPublicExponent().toByteArray();
RSAKeyValueType rsaKeyValue = new RSAKeyValueType();
rsaKeyValue.setModulus(Base64.encodeBytes(modulus).getBytes());
rsaKeyValue.setExponent(Base64.encodeBytes(exponent).getBytes());
rsaKeyValue.setModulus(Base64.encodeBytes(modulus).getBytes(GeneralConstants.SAML_CHARSET));
rsaKeyValue.setExponent(Base64.encodeBytes(exponent).getBytes(GeneralConstants.SAML_CHARSET));
return rsaKeyValue;
} else if (key instanceof DSAPublicKey) {
DSAPublicKey pubKey = (DSAPublicKey) key;
@ -273,10 +275,10 @@ public class SignatureUtil {
byte[] Y = pubKey.getY().toByteArray();
DSAKeyValueType dsaKeyValue = new DSAKeyValueType();
dsaKeyValue.setP(Base64.encodeBytes(P).getBytes());
dsaKeyValue.setQ(Base64.encodeBytes(Q).getBytes());
dsaKeyValue.setG(Base64.encodeBytes(G).getBytes());
dsaKeyValue.setY(Base64.encodeBytes(Y).getBytes());
dsaKeyValue.setP(Base64.encodeBytes(P).getBytes(GeneralConstants.SAML_CHARSET));
dsaKeyValue.setQ(Base64.encodeBytes(Q).getBytes(GeneralConstants.SAML_CHARSET));
dsaKeyValue.setG(Base64.encodeBytes(G).getBytes(GeneralConstants.SAML_CHARSET));
dsaKeyValue.setY(Base64.encodeBytes(Y).getBytes(GeneralConstants.SAML_CHARSET));
return dsaKeyValue;
}
throw logger.unsupportedType(key.toString());

View file

@ -27,9 +27,11 @@ import org.keycloak.dom.xmlsec.w3.xmldsig.X509DataType;
import org.keycloak.saml.common.ErrorCodes;
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.WSTrustConstants;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.common.util.StaxUtil;
import org.w3c.dom.Element;
import javax.xml.stream.XMLStreamWriter;
@ -77,7 +79,7 @@ public class StaxWriterUtil {
X509CertificateType cert = (X509CertificateType) obj;
StaxUtil.writeStartElement(writer, WSTrustConstants.XMLDSig.DSIG_PREFIX, WSTrustConstants.XMLDSig.X509CERT,
WSTrustConstants.XMLDSig.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(cert.getEncodedCertificate()));
StaxUtil.writeCharacters(writer, new String(cert.getEncodedCertificate(), GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}
StaxUtil.writeEndElement(writer);
@ -105,13 +107,13 @@ public class StaxWriterUtil {
// write the rsa key modulus.
byte[] modulus = type.getModulus();
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.MODULUS, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(modulus));
StaxUtil.writeCharacters(writer, new String(modulus, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
// write the rsa key exponent.
byte[] exponent = type.getExponent();
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.EXPONENT, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(exponent));
StaxUtil.writeCharacters(writer, new String(exponent, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
StaxUtil.writeEndElement(writer);
@ -126,37 +128,37 @@ public class StaxWriterUtil {
byte[] p = type.getP();
if (p != null) {
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.P, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(p));
StaxUtil.writeCharacters(writer, new String(p, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}
byte[] q = type.getQ();
if (q != null) {
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.Q, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(q));
StaxUtil.writeCharacters(writer, new String(q, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}
byte[] g = type.getG();
if (g != null) {
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.G, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(g));
StaxUtil.writeCharacters(writer, new String(g, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}
byte[] y = type.getY();
if (y != null) {
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.Y, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(y));
StaxUtil.writeCharacters(writer, new String(y, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}
byte[] seed = type.getSeed();
if (seed != null) {
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.SEED, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(seed));
StaxUtil.writeCharacters(writer, new String(seed, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}
byte[] pgen = type.getPgenCounter();
if (pgen != null) {
StaxUtil.writeStartElement(writer, prefix, WSTrustConstants.XMLDSig.PGEN_COUNTER, WSTrustConstants.DSIG_NS);
StaxUtil.writeCharacters(writer, new String(pgen));
StaxUtil.writeCharacters(writer, new String(pgen, GeneralConstants.SAML_CHARSET));
StaxUtil.writeEndElement(writer);
}

View file

@ -18,6 +18,8 @@ package org.keycloak.saml.processing.core.util;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
@ -89,7 +91,7 @@ public class JAXBUtil {
JAXBContext jc = getJAXBContext(pkgName);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_ENCODING, GeneralConstants.SAML_CHARSET_NAME);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); // Breaks signatures
return marshaller;
}

View file

@ -22,6 +22,7 @@ import org.keycloak.dom.xmlsec.w3.xmldsig.RSAKeyValueType;
import org.keycloak.dom.xmlsec.w3.xmldsig.SignatureType;
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.WSTrustConstants;
import org.keycloak.saml.common.exceptions.ParsingException;
@ -572,7 +573,7 @@ public class XMLSignatureUtil {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteArrayInputStream bais = new ByteArrayInputStream(derFormattedString.getBytes());
ByteArrayInputStream bais = new ByteArrayInputStream(derFormattedString.getBytes(GeneralConstants.SAML_CHARSET));
while (bais.available() > 0) {
cert = (X509Certificate) cf.generateCertificate(bais);
@ -603,7 +604,7 @@ public class XMLSignatureUtil {
Element childElement = (Element) node;
String tag = childElement.getLocalName();
byte[] text = childElement.getTextContent().getBytes();
byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET);
if (WSTrustConstants.XMLDSig.P.equals(tag)) {
dsa.setP(text);
@ -644,7 +645,7 @@ public class XMLSignatureUtil {
Element childElement = (Element) node;
String tag = childElement.getLocalName();
byte[] text = childElement.getTextContent().getBytes();
byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET);
if (WSTrustConstants.XMLDSig.MODULUS.equals(tag)) {
rsa.setModulus(text);
@ -674,8 +675,8 @@ public class XMLSignatureUtil {
byte[] exponent = pubKey.getPublicExponent().toByteArray();
RSAKeyValueType rsaKeyValue = new RSAKeyValueType();
rsaKeyValue.setModulus(Base64.encodeBytes(modulus).getBytes());
rsaKeyValue.setExponent(Base64.encodeBytes(exponent).getBytes());
rsaKeyValue.setModulus(Base64.encodeBytes(modulus).getBytes(GeneralConstants.SAML_CHARSET));
rsaKeyValue.setExponent(Base64.encodeBytes(exponent).getBytes(GeneralConstants.SAML_CHARSET));
return rsaKeyValue;
} else if (key instanceof DSAPublicKey) {
DSAPublicKey pubKey = (DSAPublicKey) key;
@ -685,10 +686,10 @@ public class XMLSignatureUtil {
byte[] Y = pubKey.getY().toByteArray();
DSAKeyValueType dsaKeyValue = new DSAKeyValueType();
dsaKeyValue.setP(Base64.encodeBytes(P).getBytes());
dsaKeyValue.setQ(Base64.encodeBytes(Q).getBytes());
dsaKeyValue.setG(Base64.encodeBytes(G).getBytes());
dsaKeyValue.setY(Base64.encodeBytes(Y).getBytes());
dsaKeyValue.setP(Base64.encodeBytes(P).getBytes(GeneralConstants.SAML_CHARSET));
dsaKeyValue.setQ(Base64.encodeBytes(Q).getBytes(GeneralConstants.SAML_CHARSET));
dsaKeyValue.setG(Base64.encodeBytes(G).getBytes(GeneralConstants.SAML_CHARSET));
dsaKeyValue.setY(Base64.encodeBytes(Y).getBytes(GeneralConstants.SAML_CHARSET));
return dsaKeyValue;
}
throw logger.unsupportedType(key.toString());

View file

@ -18,6 +18,7 @@ package org.keycloak.saml.processing.web.util;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.util.Base64;
import java.io.ByteArrayInputStream;
@ -42,7 +43,7 @@ public class PostBindingUtil {
* @return
*/
public static String base64Encode(String stringToEncode) throws IOException {
return Base64.encodeBytes(stringToEncode.getBytes("UTF-8"), Base64.DONT_BREAK_LINES);
return Base64.encodeBytes(stringToEncode.getBytes(GeneralConstants.SAML_CHARSET), Base64.DONT_BREAK_LINES);
}
/**

View file

@ -79,7 +79,7 @@ public class RedirectBindingSignatureUtil {
String urlEncodedRelayState = null;
if (isNotNull(relayState))
urlEncodedRelayState = URLEncoder.encode(relayState, "UTF-8");
urlEncodedRelayState = URLEncoder.encode(relayState, GeneralConstants.SAML_CHARSET_NAME);
byte[] sigValue = computeSignature(GeneralConstants.SAML_REQUEST_KEY, urlEncodedRequest, urlEncodedRelayState,
signingKey);
@ -113,7 +113,7 @@ public class RedirectBindingSignatureUtil {
String urlEncodedRelayState = null;
if (isNotNull(relayState))
urlEncodedRelayState = URLEncoder.encode(relayState, "UTF-8");
urlEncodedRelayState = URLEncoder.encode(relayState, GeneralConstants.SAML_CHARSET_NAME);
byte[] sigValue = computeSignature(GeneralConstants.SAML_RESPONSE_KEY, urlEncodedResponse, urlEncodedRelayState,
signingKey);
@ -234,7 +234,7 @@ public class RedirectBindingSignatureUtil {
addParameter(sb, GeneralConstants.SAML_SIG_ALG_REQUEST_KEY,
RedirectBindingSignatureUtil.getTokenValue(queryString, GeneralConstants.SAML_SIG_ALG_REQUEST_KEY));
return SignatureUtil.validate(sb.toString().getBytes("UTF-8"), sigValue, validatingKey);
return SignatureUtil.validate(sb.toString().getBytes(GeneralConstants.SAML_CHARSET), sigValue, validatingKey);
}
private static boolean isRequestQueryString(String queryString) {
@ -257,7 +257,7 @@ public class RedirectBindingSignatureUtil {
String algo = signingKey.getAlgorithm();
String sigAlg = SignatureUtil.getXMLSignatureAlgorithmURI(algo);
sigAlg = URLEncoder.encode(sigAlg, "UTF-8");
sigAlg = URLEncoder.encode(sigAlg, GeneralConstants.SAML_CHARSET_NAME);
addParameter(sb, GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, sigAlg);
@ -291,7 +291,7 @@ public class RedirectBindingSignatureUtil {
// SigAlg
String sigAlg = SignatureUtil.getXMLSignatureAlgorithmURI(sigAlgo);
sigAlg = URLEncoder.encode(sigAlg, "UTF-8");
sigAlg = URLEncoder.encode(sigAlg, GeneralConstants.SAML_CHARSET_NAME);
addParameter(sb, GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, sigAlg);

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.saml.processing.web.util;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.util.Base64;
import org.keycloak.saml.common.util.StringUtil;
import org.keycloak.saml.processing.api.util.DeflateUtil;
@ -43,7 +44,7 @@ public class RedirectBindingUtil {
* @throws IOException
*/
public static String urlEncode(String str) throws IOException {
return URLEncoder.encode(str, "UTF-8");
return URLEncoder.encode(str, GeneralConstants.SAML_CHARSET_NAME);
}
/**
@ -56,7 +57,7 @@ public class RedirectBindingUtil {
* @throws IOException
*/
public static String urlDecode(String str) throws IOException {
return URLDecoder.decode(str, "UTF-8");
return URLDecoder.decode(str, GeneralConstants.SAML_CHARSET_NAME);
}
/**
@ -97,7 +98,7 @@ public class RedirectBindingUtil {
* @throws IOException
*/
public static String deflateBase64URLEncode(String stringToEncode) throws IOException {
return deflateBase64URLEncode(stringToEncode.getBytes("UTF-8"));
return deflateBase64URLEncode(stringToEncode.getBytes(GeneralConstants.SAML_CHARSET));
}
/**

View file

@ -16,6 +16,8 @@
*/
package org.keycloak.saml.processing.core.parsers.saml;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
import java.io.InputStream;
@ -27,8 +29,6 @@ import static org.hamcrest.CoreMatchers.*;
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import java.io.IOException;
import java.util.Scanner;
import org.junit.Before;
import org.w3c.dom.Element;
@ -108,6 +108,38 @@ public class SAMLParserTest {
}
}
@Test
public void testSaml20AuthnResponseNonAsciiNameDefaultUtf8() throws Exception {
try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-3971-utf-8-no-header-authnresponse.xml")) {
Object parsedObject = parser.parse(st);
assertThat(parsedObject, instanceOf(ResponseType.class));
ResponseType rt = (ResponseType) parsedObject;
assertThat(rt.getAssertions().size(), is(1));
final AssertionType assertion = rt.getAssertions().get(0).getAssertion();
assertThat(assertion.getSubject().getSubType().getBaseID(), instanceOf(NameIDType.class));
NameIDType nameId = (NameIDType) assertion.getSubject().getSubType().getBaseID();
assertThat(nameId.getValue(), is("roàåאבčéèíñòøöùüßåäöü汉字"));
}
}
@Test
public void testSaml20AuthnResponseNonAsciiNameDefaultLatin2() throws Exception {
try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-3971-8859-2-in-header-authnresponse.xml")) {
Object parsedObject = parser.parse(st);
assertThat(parsedObject, instanceOf(ResponseType.class));
ResponseType rt = (ResponseType) parsedObject;
assertThat(rt.getAssertions().size(), is(1));
final AssertionType assertion = rt.getAssertions().get(0).getAssertion();
assertThat(assertion.getSubject().getSubType().getBaseID(), instanceOf(NameIDType.class));
NameIDType nameId = (NameIDType) assertion.getSubject().getSubType().getBaseID();
assertThat(nameId.getValue(), is("ročéíöüßäöü"));
}
}
@Test
public void testSaml20PostLogoutRequest() throws Exception {
try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-signed-logout-request.xml")) {

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="ISO-8859-2"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8080/sales-post-sig/saml" ID="ID_670a76c7-8506-4081-80b3-5ef16df98af8" InResponseTo="ID_cc0ff6f7-b481-4c98-9a79-481d50958290" IssueInstant="2016-12-15T12:42:49.788Z" Version="2.0">
<saml:Issuer>http://localhost:11080/auth/realms/saml-demo</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="ID_cf2e2b00-dfb0-4163-b8b5-f2d85586a235" IssueInstant="2016-12-15T12:42:49.787Z" Version="2.0">
<saml:Issuer>http://localhost:11080/auth/realms/saml-demo</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">roèéíöüßäöü</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData InResponseTo="ID_cc0ff6f7-b481-4c98-9a79-481d50958290" NotOnOrAfter="2016-12-15T12:47:47.787Z" Recipient="http://localhost:8080/sales-post-sig/saml"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2016-12-15T12:42:47.787Z" NotOnOrAfter="2016-12-15T12:43:47.787Z">
<saml:AudienceRestriction>
<saml:Audience>http://localhost:8080/sales-post-sig/</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2016-12-15T12:42:49.788Z" SessionIndex="fb5d5a23-aa34-4528-a29a-6aad8c0ef0e8">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">view-profile</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>

View file

@ -0,0 +1,30 @@
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8080/sales-post-sig/saml" ID="ID_670a76c7-8506-4081-80b3-5ef16df98af8" InResponseTo="ID_cc0ff6f7-b481-4c98-9a79-481d50958290" IssueInstant="2016-12-15T12:42:49.788Z" Version="2.0">
<saml:Issuer>http://localhost:11080/auth/realms/saml-demo</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="ID_cf2e2b00-dfb0-4163-b8b5-f2d85586a235" IssueInstant="2016-12-15T12:42:49.787Z" Version="2.0">
<saml:Issuer>http://localhost:11080/auth/realms/saml-demo</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">roàåאבčéèíñòøöùüßåäöü汉字</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData InResponseTo="ID_cc0ff6f7-b481-4c98-9a79-481d50958290" NotOnOrAfter="2016-12-15T12:47:47.787Z" Recipient="http://localhost:8080/sales-post-sig/saml"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2016-12-15T12:42:47.787Z" NotOnOrAfter="2016-12-15T12:43:47.787Z">
<saml:AudienceRestriction>
<saml:Audience>http://localhost:8080/sales-post-sig/</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2016-12-15T12:42:49.788Z" SessionIndex="fb5d5a23-aa34-4528-a29a-6aad8c0ef0e8">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">view-profile</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>