opensaml encoding problems with xml to base64
Jason Novotny
jason.novotny at gmail.com
Tue Nov 4 23:29:47 EST 2014
Here are some additional strange details, when I get the BASE64
SAMLRequest from the identity provider, it is:
AX0Cgv08P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/PjxzYW1scDpBdXRoblJlcXVlc3QgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgQXNzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlSW5kZXg9IjAiIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9hbHBoYS5hZHZpc29yc2VydmljZXMuY29tL3NlcnZsZXQvYWR2aXNvci9zYW1sc3NvbG9naW4vIiBGb3JjZUF1dGhuPSJ0cnVlIiBJRD0iX2FiODIwM2U3MmI1YmU0Y2UiIElzUGFzc2l2ZT0idHJ1ZSIgSXNzdWVJbnN0YW50PSIyMDE0LTA5LTA4VDE2OjUxOjI5LjY0OVoiIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUmVkaXJlY3QiIFByb3ZpZGVyTmFtZT0iaHR0cHM6Ly93d3cuYWR2aXNvcnNlcnZpY2VzLmNvbS8iIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHBzOi8vd3d3LmFkdmlzb3JzZXJ2aWNlcy5jb20vPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEFsbG93Q3JlYXRlPSJ0cnVlIi8+PC9zYW1scDpBdXRoblJlcXVlc3Q+
Now I print that to a file:
byte[] samlBytes = Base64.decode(samlResponseStr);
FileOutputStream fos = new FileOutputStream("/tmp/test");
fos.write(samlBytes);
fos.close();
And the contents of the /tmp/test file are:
^A}^B<82><FD><?xml version="1.0" encoding="UTF-8"?><samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceIndex="0"
AssertionConsumerServiceURL="https://alpha.advisorservices.com/servlet/advisor/samlssologin/"
ForceAuthn="true" ID="_ab8203e72b5be4ce" IsPassive="true"
IssueInstant="2014-09-08T16:51:29.649Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
ProviderName="https://www.advisorservices.com/"
Version="2.0"><saml:Issuer
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://www.advisorservices.com/</saml:Issuer><samlp:NameIDPolicy
AllowCreate="true"/></samlp:AuthnRequest>
As you can see, there are 5 bytes at the beginning that I cleared away
with my parseDocument1 method, but of course that's a terrible hack. I
just can't quite understand how those bytes get there..
Thanks, Jason
On 11/4/14, 8:16 PM, Jason Novotny wrote:
> 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