1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.saml2.binding.encoding;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.opensaml.Configuration;
24 import org.opensaml.common.SAMLObject;
25 import org.opensaml.common.SignableSAMLObject;
26 import org.opensaml.common.binding.SAMLMessageContext;
27 import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
28 import org.opensaml.saml2.core.StatusResponseType;
29 import org.opensaml.saml2.metadata.Endpoint;
30 import org.opensaml.util.URLBuilder;
31 import org.opensaml.ws.message.encoder.BaseMessageEncoder;
32 import org.opensaml.ws.message.encoder.MessageEncodingException;
33 import org.opensaml.xml.XMLObjectBuilder;
34 import org.opensaml.xml.io.Marshaller;
35 import org.opensaml.xml.io.MarshallingException;
36 import org.opensaml.xml.security.SecurityException;
37 import org.opensaml.xml.security.SecurityHelper;
38 import org.opensaml.xml.security.credential.Credential;
39 import org.opensaml.xml.signature.Signature;
40 import org.opensaml.xml.signature.SignatureException;
41 import org.opensaml.xml.signature.Signer;
42 import org.opensaml.xml.util.DatatypeHelper;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47
48
49 public abstract class BaseSAML2MessageEncoder extends BaseMessageEncoder implements SAMLMessageEncoder {
50
51
52 private final Logger log = LoggerFactory.getLogger(BaseSAML2MessageEncoder.class);
53
54
55 private List<String> allowedURLSchemes;
56
57 public BaseSAML2MessageEncoder(){
58 super();
59 setAllowedURLSchemes(new String[] { "http", "https" });
60 }
61
62
63
64
65
66
67 public List<String> getAllowedURLSchemes() {
68 return allowedURLSchemes;
69 }
70
71
72
73
74
75
76
77 public void setAllowedURLSchemes(String[] schemes) {
78 if (schemes == null || schemes.length == 0) {
79 allowedURLSchemes = Collections.emptyList();
80 } else {
81 List<String> temp = new ArrayList<String>();
82 for (String scheme : schemes) {
83 temp.add(scheme);
84 }
85 allowedURLSchemes = Collections.unmodifiableList(temp);
86 }
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100 protected URLBuilder getEndpointURL(SAMLMessageContext messageContext) throws MessageEncodingException {
101 Endpoint endpoint = messageContext.getPeerEntityEndpoint();
102 if (endpoint == null) {
103 throw new MessageEncodingException("Endpoint for relying party was null.");
104 }
105
106 URLBuilder urlBuilder;
107 if (messageContext.getOutboundMessage() instanceof StatusResponseType
108 && !DatatypeHelper.isEmpty(endpoint.getResponseLocation())) {
109 urlBuilder = new URLBuilder(endpoint.getResponseLocation());
110 } else {
111 if (DatatypeHelper.isEmpty(endpoint.getLocation())) {
112 throw new MessageEncodingException("Relying party endpoint location was null or empty.");
113 }
114 urlBuilder = new URLBuilder(endpoint.getLocation());
115 }
116
117 if(!getAllowedURLSchemes().contains(urlBuilder.getScheme())){
118 throw new MessageEncodingException("Relying party endpoint used the untrusted URL scheme " + urlBuilder.getScheme());
119 }
120 return urlBuilder;
121 }
122
123
124
125
126
127
128
129
130 protected boolean checkRelayState(String relayState) {
131 if (!DatatypeHelper.isEmpty(relayState)) {
132 if (relayState.getBytes().length > 80) {
133 log.warn("Relay state exceeds 80 bytes, some application may not support this.");
134 }
135
136 return true;
137 }
138
139 return false;
140 }
141
142
143
144
145
146
147
148 protected void setResponseDestination(SAMLObject outboundMessage, String endpointURL) {
149 if (outboundMessage instanceof StatusResponseType) {
150 ((StatusResponseType) outboundMessage).setDestination(endpointURL);
151 }
152 }
153
154
155
156
157
158
159
160
161 @SuppressWarnings("unchecked")
162 protected void signMessage(SAMLMessageContext messageContext) throws MessageEncodingException {
163 SAMLObject outboundSAML = messageContext.getOutboundSAMLMessage();
164 Credential signingCredential = messageContext.getOuboundSAMLMessageSigningCredential();
165
166 if (outboundSAML instanceof SignableSAMLObject && signingCredential != null) {
167 SignableSAMLObject signableMessage = (SignableSAMLObject) outboundSAML;
168
169 XMLObjectBuilder<Signature> signatureBuilder = Configuration.getBuilderFactory().getBuilder(
170 Signature.DEFAULT_ELEMENT_NAME);
171 Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
172
173 signature.setSigningCredential(signingCredential);
174 try {
175
176
177 SecurityHelper.prepareSignatureParams(signature, signingCredential, null, null);
178 } catch (SecurityException e) {
179 throw new MessageEncodingException("Error preparing signature for signing", e);
180 }
181
182 signableMessage.setSignature(signature);
183
184 try {
185 Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(signableMessage);
186 if (marshaller == null) {
187 throw new MessageEncodingException("No marshaller registered for "
188 + signableMessage.getElementQName() + ", unable to marshall in preperation for signing");
189 }
190 marshaller.marshall(signableMessage);
191
192 Signer.signObject(signature);
193 } catch (MarshallingException e) {
194 log.error("Unable to marshall protocol message in preparation for signing", e);
195 throw new MessageEncodingException("Unable to marshall protocol message in preparation for signing", e);
196 } catch (SignatureException e) {
197 log.error("Unable to sign protocol message", e);
198 throw new MessageEncodingException("Unable to sign protocol message", e);
199 }
200 }
201 }
202 }