opensaml encoding problems with xml to base64
Jason Novotny
jason.novotny at gmail.com
Tue Nov 4 23:16:37 EST 2014
Hi,
I'm having problems integrating SSO inbound and outbound with my
Identity Provider in that the AuthnRequest I get back will have some
strange bytes that I've had to write a hack to deal with and the
SAMLResponse that I send out apparently is not decoded into proper XML.
Googling around I see a number of ways to marshall/unmarshall and I'd
like to know which way is the approved way.
Here's the code I was using for converting a base64 AuthnRequest into a
org.w3c.dom.Document:
private Document parseDocument(String encoded) throws
VeoSSOServiceException {
try {
return parseDocument1(encoded);
} catch (Exception e) {
try {
return parseDocument2(encoded);
} catch (Exception ef) {
// god help us
}
}
throw new VeoSSOServiceException("Can't parse the encoded data!");
}
private Document parseDocument1(String encoded) throws Exception {
byte[] base64DecodedResponse = Base64.decode(encoded);
ByteArrayInputStream is = new
ByteArrayInputStream(base64DecodedResponse);
DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder =
documentBuilderFactory.newDocumentBuilder();
StringWriter writer = new StringWriter();
IOUtils.copy(is, writer, "UTF-8");
encoded = writer.toString().replaceAll("[^\\p{ASCII}]", "");
//encoded =
writer.toString().substring(writer.toString().indexOf("<"));
is = new ByteArrayInputStream(encoded.getBytes());
Document document = docBuilder.parse(is);
return document;
}
private Document parseDocument2(String encoded) throws Exception {
byte[] samlBytes = Base64.decode(encoded);
Inflater inflater = new Inflater(true);
inflater.setInput(samlBytes);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
if (count == 0)
break;
stream.write(buffer, 0, count);
}
stream.close();
samlBytes = stream.toByteArray();
DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder =
documentBuilderFactory.newDocumentBuilder();
ByteArrayInputStream is = new ByteArrayInputStream(samlBytes);
Document document = docBuilder.parse(is);
return document;
}
I'm also wondering how I should be encoding my SAMLresponse. Here is the
code I'm currently using:
public VeoSSOResult parseSAMLRequest(String samlRequestStr) throws
VeoSSOServiceException {
try {
VeoSSOResult veoSSOResult = new VeoSSOResult();
Document document = parseDocument(samlRequestStr);
Element metadataRoot = document.getDocumentElement();
QName qName = new QName(metadataRoot.getNamespaceURI(),
metadataRoot.getLocalName(), "saml2p");
// get an unmarshaller
Unmarshaller unmarshaller =
Configuration.getUnmarshallerFactory().getUnmarshaller(qName);
Object object = unmarshaller.unmarshall(metadataRoot);
AuthnRequest authnRequest = (AuthnRequest) object;
String response = createSAMLResponse(authnRequest);
veoSSOResult.setBase64(response);
veoSSOResult.setDestinationURL(authnRequest.getAssertionConsumerServiceURL());
return veoSSOResult;
} catch (Exception e) {
throw new VeoSSOServiceException("No good", e);
}
}
private void authenticateRequest(AuthnRequest authnRequest) {
// TODO anything we care about validating from AuthnRequest input?
}
public String createSAMLResponse(AuthnRequest authnRequest) throws
VeoSSOServiceException, IOException {
authenticateRequest(authnRequest);
VeoSSOResponseInfo info = null;
try {
info = new VeoSSOResponseInfo();
} catch (Exception e) {
throw new VeoSSOServiceException("Unable to initiate:", e);
}
final SAMLObjectBuilder<Response> responseBuilder =
(SAMLObjectBuilder<Response>)
builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
final Response responseMessage = responseBuilder.buildObject();
responseMessage.setID(createID());
responseMessage.setVersion(SAMLVersion.VERSION_20);
responseMessage.setIssueInstant(new DateTime());
responseMessage.setInResponseTo(authnRequest.getID());
responseMessage.setDestination(authnRequest.getAssertionConsumerServiceURL());
final Issuer issuer = buildIssuer(info);
responseMessage.setIssuer(issuer);
final Status status = buildStatus(info);
responseMessage.setStatus(status);
final Assertion assertion = buildAssertion(info);
responseMessage.getAssertions().add(assertion);
ResponseMarshaller marshaller = new ResponseMarshaller();
Element plain = null;
String encodedRequestMessage;
try {
plain = marshaller.marshall(responseMessage);
String samlResponse = XMLHelper.nodeToString(plain);
byte[] xmlBytes = samlResponse.getBytes("UTF-8");
String base64Encoded = Base64.encodeBytes(xmlBytes);
encodedRequestMessage =
Base64.encodeBytes(samlResponse.getBytes(),
Base64.DONT_BREAK_LINES);
return base64Encoded;
} catch (MarshallingException e) {
throw new VeoSSOServiceException("Unable to marshall
SAMLResponse:", e);
}
}
Any help is greatly appreciated!
Thanks, Jason
More information about the dev
mailing list