1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.dom.saml;
21
22 import java.security.MessageDigest;
23 import java.security.Principal;
24 import java.security.PublicKey;
25 import java.security.cert.Certificate;
26 import java.security.cert.X509Certificate;
27 import java.util.ArrayList;
28 import java.util.List;
29
30 import org.apache.wss4j.common.ext.WSSecurityException;
31 import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal;
32 import org.apache.wss4j.common.saml.OpenSAMLUtil;
33 import org.apache.wss4j.common.saml.SAMLKeyInfo;
34 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
35 import org.apache.wss4j.dom.WSConstants;
36 import org.apache.wss4j.dom.WSDataRef;
37 import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
38 import org.apache.wss4j.dom.handler.WSHandlerResult;
39 import org.w3c.dom.Element;
40
41
42
43
44 public final class DOMSAMLUtil {
45
46 private static final org.slf4j.Logger LOG =
47 org.slf4j.LoggerFactory.getLogger(DOMSAMLUtil.class);
48
49 private DOMSAMLUtil() {
50
51 }
52
53 public static void validateSAMLResults(
54 WSHandlerResult handlerResults,
55 Certificate[] tlsCerts,
56 Element body
57 ) throws WSSecurityException {
58 List<WSSecurityEngineResult> samlResults = new ArrayList<>();
59 if (handlerResults.getActionResults().containsKey(WSConstants.ST_SIGNED)) {
60 samlResults.addAll(handlerResults.getActionResults().get(WSConstants.ST_SIGNED));
61 }
62 if (handlerResults.getActionResults().containsKey(WSConstants.ST_UNSIGNED)) {
63 samlResults.addAll(handlerResults.getActionResults().get(WSConstants.ST_UNSIGNED));
64 }
65
66 if (samlResults.isEmpty()) {
67 return;
68 }
69
70 List<WSSecurityEngineResult> signedResults = new ArrayList<>();
71 if (handlerResults.getActionResults().containsKey(WSConstants.SIGN)) {
72 signedResults.addAll(handlerResults.getActionResults().get(WSConstants.SIGN));
73 }
74 if (handlerResults.getActionResults().containsKey(WSConstants.UT_SIGN)) {
75 signedResults.addAll(handlerResults.getActionResults().get(WSConstants.UT_SIGN));
76 }
77
78 for (WSSecurityEngineResult samlResult : samlResults) {
79 SamlAssertionWrapper assertionWrapper =
80 (SamlAssertionWrapper)samlResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
81
82 if (!checkHolderOfKey(assertionWrapper, signedResults, tlsCerts)) {
83 LOG.warn("Assertion fails holder-of-key requirements");
84 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
85 }
86 if (!checkSenderVouches(assertionWrapper, tlsCerts, body, signedResults)) {
87 LOG.warn("Assertion fails sender-vouches requirements");
88 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
89 }
90 }
91
92 }
93
94
95
96
97
98
99
100
101
102
103 public static boolean checkHolderOfKey(
104 SamlAssertionWrapper assertionWrapper,
105 List<WSSecurityEngineResult> signedResults,
106 Certificate[] tlsCerts
107 ) {
108 List<String> confirmationMethods = assertionWrapper.getConfirmationMethods();
109 boolean isHolderOfKey = false;
110 for (String confirmationMethod : confirmationMethods) {
111 if (OpenSAMLUtil.isMethodHolderOfKey(confirmationMethod)) {
112 isHolderOfKey = true;
113 break;
114 }
115 }
116
117 if (isHolderOfKey) {
118 if (tlsCerts == null && (signedResults == null || signedResults.isEmpty())) {
119 return false;
120 }
121 SAMLKeyInfo subjectKeyInfo = assertionWrapper.getSubjectKeyInfo();
122 if (!compareCredentials(subjectKeyInfo, signedResults, tlsCerts)) {
123 return false;
124 }
125 }
126 return true;
127 }
128
129
130
131
132
133
134
135
136
137 public static boolean compareCredentials(
138 SAMLKeyInfo subjectKeyInfo,
139 List<WSSecurityEngineResult> signedResults,
140 Certificate[] tlsCerts
141 ) {
142 X509Certificate[] subjectCerts = subjectKeyInfo.getCerts();
143 PublicKey subjectPublicKey = subjectKeyInfo.getPublicKey();
144 byte[] subjectSecretKey = subjectKeyInfo.getSecret();
145
146
147
148
149 if (tlsCerts != null && tlsCerts.length > 0 && subjectCerts != null
150 && subjectCerts.length > 0 && tlsCerts[0].equals(subjectCerts[0])) {
151 return true;
152 } else if (tlsCerts != null && tlsCerts.length > 0 && subjectPublicKey != null
153 && tlsCerts[0].getPublicKey().equals(subjectPublicKey)) {
154 return true;
155 }
156
157 if (subjectPublicKey == null && subjectCerts != null && subjectCerts.length > 0) {
158 subjectPublicKey = subjectCerts[0].getPublicKey();
159 }
160
161
162
163
164 for (WSSecurityEngineResult signedResult : signedResults) {
165 X509Certificate[] certs =
166 (X509Certificate[])signedResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
167 PublicKey publicKey =
168 (PublicKey)signedResult.get(WSSecurityEngineResult.TAG_PUBLIC_KEY);
169 byte[] secretKey =
170 (byte[])signedResult.get(WSSecurityEngineResult.TAG_SECRET);
171 if (certs != null && certs.length > 0 && subjectCerts != null
172 && subjectCerts.length > 0 && certs[0].equals(subjectCerts[0])) {
173 return true;
174 }
175 if (publicKey != null && publicKey.equals(subjectPublicKey)) {
176 return true;
177 }
178 if (checkSecretKey(secretKey, subjectSecretKey, signedResult)) {
179 return true;
180 }
181 }
182 return false;
183 }
184
185 private static boolean checkSecretKey(
186 byte[] secretKey,
187 byte[] subjectSecretKey,
188 WSSecurityEngineResult signedResult
189 ) {
190 if (secretKey != null && subjectSecretKey != null) {
191 if (MessageDigest.isEqual(secretKey, subjectSecretKey)) {
192 return true;
193 } else {
194 Principal principal =
195 (Principal)signedResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
196 if (principal instanceof WSDerivedKeyTokenPrincipal) {
197 secretKey = ((WSDerivedKeyTokenPrincipal)principal).getSecret();
198 if (MessageDigest.isEqual(secretKey, subjectSecretKey)) {
199 return true;
200 }
201 }
202 }
203 }
204 return false;
205 }
206
207
208
209
210
211 public static boolean checkSenderVouches(
212 SamlAssertionWrapper assertionWrapper,
213 Certificate[] tlsCerts,
214 Element body,
215 List<WSSecurityEngineResult> signed
216 ) {
217
218
219
220
221 if (tlsCerts != null && tlsCerts.length > 0) {
222 return true;
223 }
224
225 List<String> confirmationMethods = assertionWrapper.getConfirmationMethods();
226 boolean isSenderVouches = false;
227 for (String confirmationMethod : confirmationMethods) {
228 if (OpenSAMLUtil.isMethodSenderVouches(confirmationMethod)) {
229 isSenderVouches = true;
230 break;
231 }
232 }
233
234 if (isSenderVouches) {
235 if (signed == null || signed.isEmpty()) {
236 return false;
237 }
238 if (!checkAssertionAndBodyAreSigned(assertionWrapper, body, signed)) {
239 return false;
240 }
241 }
242 return true;
243 }
244
245
246
247
248
249
250
251
252 private static boolean checkAssertionAndBodyAreSigned(
253 SamlAssertionWrapper assertionWrapper,
254 Element body,
255 List<WSSecurityEngineResult> signed
256 ) {
257 for (WSSecurityEngineResult signedResult : signed) {
258 @SuppressWarnings("unchecked")
259 List<WSDataRef> sl =
260 (List<WSDataRef>)signedResult.get(
261 WSSecurityEngineResult.TAG_DATA_REF_URIS
262 );
263 boolean assertionIsSigned = false;
264 boolean bodyIsSigned = false;
265 if (sl != null) {
266 for (WSDataRef dataRef : sl) {
267 Element se = dataRef.getProtectedElement();
268 if (se == assertionWrapper.getElement()) {
269 assertionIsSigned = true;
270 }
271 if (se == body) {
272 bodyIsSigned = true;
273 }
274 if (assertionIsSigned && bodyIsSigned) {
275 return true;
276 }
277 }
278 }
279 }
280 return false;
281 }
282
283
284 }