-
Notifications
You must be signed in to change notification settings - Fork 6.4k
/
SAML2Signature.java
executable file
·229 lines (201 loc) · 7.36 KB
/
SAML2Signature.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.saml.processing.api.saml.v2.sig;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.core.util.SignatureUtilTransferObject;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.parsers.ParserConfigurationException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
/**
* Class that deals with SAML2 Signature
*
* @author Anil.Saldhana@redhat.com
* @author alessio.soldano@jboss.com
* @since May 26, 2009
*/
public class SAML2Signature {
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
private static final String ID_ATTRIBUTE_NAME = "ID";
private String signatureMethod = SignatureMethod.RSA_SHA1;
private String digestMethod = DigestMethod.SHA1;
private Node sibling;
/**
* Set the X509Certificate if X509Data is needed in signed info
*/
private X509Certificate x509Certificate;
public String getSignatureMethod() {
return signatureMethod;
}
public void setSignatureMethod(String signatureMethod) {
this.signatureMethod = signatureMethod;
}
public String getDigestMethod() {
return digestMethod;
}
public void setDigestMethod(String digestMethod) {
this.digestMethod = digestMethod;
}
public void setNextSibling(Node sibling) {
this.sibling = sibling;
}
/**
* Set to false, if you do not want to include keyinfo in the signature
*
* @param val
*
* @since v2.0.1
*/
public void setSignatureIncludeKeyInfo(boolean val) {
if (!val) {
XMLSignatureUtil.setIncludeKeyInfoInSignature(false);
}
}
/**
* Set the {@link X509Certificate} if you desire
* to have the SignedInfo have X509 Data
*
* This method needs to be called before any of the sign methods.
*
* @param x509Certificate
*
* @since v2.5.0
*/
public void setX509Certificate(X509Certificate x509Certificate) {
this.x509Certificate = x509Certificate;
}
/**
* Sign an Document at the root
*
* @param keyPair Key Pair
*
* @return
*
* @throws ParserConfigurationException
* @throws XMLSignatureException
* @throws MarshalException
* @throws GeneralSecurityException
*/
public Document sign(Document doc, String referenceID, String keyId, KeyPair keyPair, String canonicalizationMethodType) throws ParserConfigurationException,
GeneralSecurityException, MarshalException, XMLSignatureException {
String referenceURI = "#" + referenceID;
configureIdAttribute(doc);
if (sibling != null) {
SignatureUtilTransferObject dto = new SignatureUtilTransferObject();
dto.setDocumentToBeSigned(doc);
dto.setKeyId(keyId);
dto.setKeyPair(keyPair);
dto.setDigestMethod(digestMethod);
dto.setSignatureMethod(signatureMethod);
dto.setReferenceURI(referenceURI);
dto.setNextSibling(sibling);
if (x509Certificate != null) {
dto.setX509Certificate(x509Certificate);
}
return XMLSignatureUtil.sign(dto, canonicalizationMethodType);
}
return XMLSignatureUtil.sign(doc, keyId, keyPair, digestMethod, signatureMethod, referenceURI, canonicalizationMethodType);
}
/**
* Sign a SAML Document
*
* @param samlDocument
* @param keypair
*
* @throws org.keycloak.saml.common.exceptions.ProcessingException
*/
public void signSAMLDocument(Document samlDocument, String keyId, KeyPair keypair, String canonicalizationMethodType) throws ProcessingException {
// Get the ID from the root
String id = samlDocument.getDocumentElement().getAttribute(ID_ATTRIBUTE_NAME);
try {
sign(samlDocument, id, keyId, keypair, canonicalizationMethodType);
} catch (Exception e) {
throw new ProcessingException(logger.signatureError(e));
}
}
/**
* Validate the SAML2 Document
*
* @param signedDocument
* @param publicKey
*
* @return
*
* @throws ProcessingException
*/
public boolean validate(Document signedDocument, PublicKey publicKey) throws ProcessingException {
try {
configureIdAttribute(signedDocument);
return XMLSignatureUtil.validate(signedDocument, publicKey);
} catch (MarshalException me) {
throw new ProcessingException(logger.signatureError(me));
} catch (XMLSignatureException xse) {
throw new ProcessingException(logger.signatureError(xse));
}
}
/**
* Given a {@link Document}, find the {@link Node} which is the sibling of the Issuer element
*
* @param doc
*
* @return
*/
public Node getNextSiblingOfIssuer(Document doc) {
// Find the sibling of Issuer
NodeList nl = doc.getElementsByTagNameNS(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get());
if (nl.getLength() > 0) {
Node issuer = nl.item(0);
return issuer.getNextSibling();
}
return null;
}
/**
* <p>
* Sets the IDness of the ID attribute. Santuario 1.5.1 does not assumes IDness based on attribute names anymore.
* This
* method should be called before signing/validating a saml document.
* </p>
*
* @param document SAML document to have its ID attribute configured.
*/
private void configureIdAttribute(Document document) {
// Estabilish the IDness of the ID attribute.
document.getDocumentElement().setIdAttribute(ID_ATTRIBUTE_NAME, true);
NodeList nodes = document.getElementsByTagNameNS(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
JBossSAMLConstants.ASSERTION.get());
for (int i = 0; i < nodes.getLength(); i++) {
Node n = nodes.item(i);
if (n instanceof Element) {
((Element) n).setIdAttribute(ID_ATTRIBUTE_NAME, true);
}
}
}
}