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.io.UnsupportedEncodingException;
20
21 import org.apache.velocity.VelocityContext;
22 import org.apache.velocity.app.VelocityEngine;
23 import org.opensaml.common.binding.SAMLMessageContext;
24 import org.opensaml.common.xml.SAMLConstants;
25 import org.opensaml.ws.message.encoder.MessageEncodingException;
26 import org.opensaml.ws.transport.http.HTTPTransportUtils;
27 import org.opensaml.xml.Configuration;
28 import org.opensaml.xml.io.Marshaller;
29 import org.opensaml.xml.io.MarshallingException;
30 import org.opensaml.xml.security.SecurityConfiguration;
31 import org.opensaml.xml.security.SecurityException;
32 import org.opensaml.xml.security.SecurityHelper;
33 import org.opensaml.xml.security.SigningUtil;
34 import org.opensaml.xml.security.credential.Credential;
35 import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
36 import org.opensaml.xml.signature.KeyInfo;
37 import org.opensaml.xml.util.Base64;
38 import org.opensaml.xml.util.DatatypeHelper;
39 import org.opensaml.xml.util.XMLHelper;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46
47
48
49
50
51
52
53
54 public class HTTPPostSimpleSignEncoder extends HTTPPostEncoder {
55
56
57 private final Logger log = LoggerFactory.getLogger(HTTPPostSimpleSignEncoder.class);
58
59
60
61
62
63 private boolean signProtocolMessageWithXMLDSIG;
64
65
66
67
68
69
70
71 public HTTPPostSimpleSignEncoder(VelocityEngine engine, String templateId) {
72 super(engine, templateId);
73 signProtocolMessageWithXMLDSIG = false;
74 }
75
76
77
78
79
80
81
82
83
84 public HTTPPostSimpleSignEncoder(VelocityEngine engine, String templateId, boolean signXMLProtocolMessage) {
85 super(engine, templateId);
86 signProtocolMessageWithXMLDSIG = signXMLProtocolMessage;
87 }
88
89
90 public String getBindingURI() {
91 return SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI;
92 }
93
94
95 protected void signMessage(SAMLMessageContext messageContext) throws MessageEncodingException {
96 if (signProtocolMessageWithXMLDSIG) {
97 super.signMessage(messageContext);
98 }
99 }
100
101
102 protected void populateVelocityContext(VelocityContext velocityContext, SAMLMessageContext messageContext,
103 String endpointURL) throws MessageEncodingException {
104
105 super.populateVelocityContext(velocityContext, messageContext, endpointURL);
106
107 Credential signingCredential = messageContext.getOuboundSAMLMessageSigningCredential();
108 if (signingCredential == null) {
109 log.debug("No signing credential was supplied, skipping HTTP-Post simple signing");
110 return;
111 }
112
113
114
115 String sigAlgURI = getSignatureAlgorithmURI(signingCredential, null);
116 velocityContext.put("SigAlg", sigAlgURI);
117
118 String formControlData = buildFormDataToSign(velocityContext, sigAlgURI);
119 velocityContext.put("Signature", generateSignature(signingCredential, sigAlgURI, formControlData));
120
121 KeyInfoGenerator kiGenerator = SecurityHelper.getKeyInfoGenerator(signingCredential, null, null);
122 if (kiGenerator != null) {
123 String kiBase64 = buildKeyInfo(signingCredential, kiGenerator);
124 if (!DatatypeHelper.isEmpty(kiBase64)) {
125 velocityContext.put("KeyInfo", kiBase64);
126 }
127 }
128 }
129
130
131
132
133
134
135
136
137
138 protected String buildKeyInfo(Credential signingCredential, KeyInfoGenerator kiGenerator)
139 throws MessageEncodingException {
140
141 try {
142 KeyInfo keyInfo = kiGenerator.generate(signingCredential);
143 if (keyInfo != null) {
144 Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(keyInfo);
145 if (marshaller == null) {
146 log.error("No KeyInfo marshaller available from configuration");
147 throw new MessageEncodingException("No KeyInfo marshaller was configured");
148 }
149 String kiXML = XMLHelper.nodeToString(marshaller.marshall(keyInfo));
150 String kiBase64 = Base64.encodeBytes(kiXML.getBytes(), Base64.DONT_BREAK_LINES);
151 return kiBase64;
152 } else {
153 return null;
154 }
155 } catch (SecurityException e) {
156 log.error("Error generating KeyInfo from signing credential", e);
157 throw new MessageEncodingException("Error generating KeyInfo from signing credential", e);
158 } catch (MarshallingException e) {
159 log.error("Error marshalling KeyInfo based on signing credential", e);
160 throw new MessageEncodingException("Error marshalling KeyInfo based on signing credential", e);
161 }
162 }
163
164
165
166
167
168
169
170
171
172
173 protected String buildFormDataToSign(VelocityContext velocityContext, String sigAlgURI) {
174 StringBuilder builder = new StringBuilder();
175
176 boolean isRequest = false;
177 if (velocityContext.get("SAMLRequest") != null) {
178 isRequest = true;
179 }
180
181 String msgB64;
182 if (isRequest) {
183 msgB64 = (String) velocityContext.get("SAMLRequest");
184 } else {
185 msgB64 = (String) velocityContext.get("SAMLResponse");
186 }
187
188 String msg = null;
189 try {
190 msg = new String(Base64.decode(msgB64), "UTF-8");
191 } catch (UnsupportedEncodingException e) {
192
193 }
194
195 if (isRequest) {
196 builder.append("SAMLRequest=" + msg);
197 } else {
198 builder.append("SAMLResponse=" + msg);
199 }
200
201 if (velocityContext.get("RelayState") != null) {
202 builder.append("&RelayState=" + HTTPTransportUtils.urlDecode((String) velocityContext.get("RelayState")));
203 }
204
205 builder.append("&SigAlg=" + sigAlgURI);
206
207 return builder.toString();
208 }
209
210
211
212
213
214
215
216
217
218
219
220 protected String getSignatureAlgorithmURI(Credential credential, SecurityConfiguration config)
221 throws MessageEncodingException {
222
223 SecurityConfiguration secConfig;
224 if (config != null) {
225 secConfig = config;
226 } else {
227 secConfig = Configuration.getGlobalSecurityConfiguration();
228 }
229
230 String signAlgo = secConfig.getSignatureAlgorithmURI(credential);
231
232 if (signAlgo == null) {
233 throw new MessageEncodingException("The signing credential's algorithm URI could not be derived");
234 }
235
236 return signAlgo;
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250 protected String generateSignature(Credential signingCredential, String algorithmURI, String formData)
251 throws MessageEncodingException {
252
253 log.debug(String.format(
254 "Generating signature with key type '%s', algorithm URI '%s' over form control string '%s'",
255 SecurityHelper.extractSigningKey(signingCredential).getAlgorithm(), algorithmURI, formData));
256
257 String b64Signature = null;
258 try {
259 byte[] rawSignature = SigningUtil.signWithURI(signingCredential, algorithmURI, formData.getBytes("UTF-8"));
260 b64Signature = Base64.encodeBytes(rawSignature, Base64.DONT_BREAK_LINES);
261 log.debug("Generated digital signature value (base64-encoded) {}", b64Signature);
262 } catch (SecurityException e) {
263 log.error("Error during URL signing process", e);
264 throw new MessageEncodingException("Unable to sign form control string", e);
265 } catch (UnsupportedEncodingException e) {
266
267 }
268
269 return b64Signature;
270 }
271
272 }