File certificateFile = new
File("C:\\ginashare\\adfs_token_trust.cer");
FileInputStream certInputStream =
new FileInputStream(certificateFile);
CertificateFactory certificateFactory =
CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)
certificateFactory
.generateCertificate(certInputStream);
// pull out the public key part of the certificate into a
// KeySpec
publicKeySpec = new
X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// generate public key to validate signatures
PublicKey publicKey =
keyFactory.generatePublic(publicKeySpec);
// create credentials
BasicX509Credential publicCredential = new
BasicX509Credential();
// add public key value
publicCredential.setPublicKey(publicKey);
// create SignatureValidator
signatureValidator = new
SignatureValidator(publicCredential);
signatureValidator.validate(signature);
Thanks.
Gina
The goal of the validator is to verify the signature. Trust management (doing anything with the certificate) is separate. There is an extensive body of code for that, and we suggest you abandon any notions of certificate evaluation in favor of using SAML metadata for key comparison.
-- Scott
Think of it this way. You are going to acquire that metadata file (and
certificate) via some out of band process. There will be some sort of
business arrangement between you and the other party. All set up and
handled outside of the technical arena. You don't need that certificate to
be signed and trusted by a commercial CA. You have an arrangement with the
partner and having established that arrangement, you already implicitly
trust them. Why pay a commercial CA? You do not gain anything by doing so.
Paul
This is an extremely complex problem, there are no simple "just check this" answers. If you wanted to use PKIX, then you *need* to do PKIX. That's far more than just checking a date. You have to do path validation, possibly check various extensions, implement a revocation strategy, etc. You also need a mechanism to bind certificate DNs to SAML issuers.
Or you can implement solutions based on SAML metadata to exchange key material. There is a standard for this, implemented at some level of the code base, possibly only in Shibboleth.
http://wiki.oasis-open.org/security/SAML2MetadataIOP
See also:
https://wiki.shibboleth.net/confluence/display/SHIB2/TrustManagement
-- Scott
On 4/28/11 11:06 AM, Gina Choi wrote:
> Thanks Paul and Scott for your response. I thought that
> signatureValidator.validate(signature) handles everything.
No, it just does the simple cryptographic validation of the signature
against the supplied key.
> By the way, do you
> have any recommendation on dealing with trust management? For example, what
> kind of items do I need to check except expiration date?
>
There's a pretty decent example in the wiki page on XML Signature,
illustrating the use of SAML metadata and the explicit key
metadata-based trust engine. The TrustEngine is the abstraction in
OpenSAML that both cryptographically validates a token as well as
performs trust evaluation. (The signature ones for example internally
make use of the SignatureValidator).
https://wiki.shibboleth.net/confluence/display/OpenSAML/OSTwoUserManJavaDSIG
It's the very last example on that page.
In the SAML metadata trust model that Scott refers to, you don't care
about any data in the certificate other than the public key. The
validity (and expiration, etc) of the binding of the key to the SAML
entity is expressed by the metadata itself. X.509/PKIX-style PKI
concepts therefore don't apply there. That's the model that we use and
advocate predominantly in Shibboleth. If you absolutely, positively
have to do X.509/PKIX style trust evaluation, we have code for that too.
The following is my initial code(definitely I will refactor it later) to
decode the string(I don't know if it is a ADFS specific, the SAML token was
encoded with Base64) and validating signature(I probably try explicit key
signature trust engine later as your suggestion). Before I go deep into wrong
direction, someone could check it for me. I got some part of the code from
Internet. By the way, response.getSignature() returns a null, so I had to get
assertion object out of response object to get a Singature. I did
assertion.getSignature() to get a Signature object. I don't know if it is
caused by ADFS implementation.
// args[0].itemAt(0).getStringValue() returns Base64 encode SAML
token string
byte[] decoded =
Base64.decodeBase64(args[0].itemAt(0).getStringValue());
String decodedSamlToken = new String(decoded);
Response response = null;
Assertion assertion = null;
// FileUtils.writeStringToFile(responseFile, decodedString);
try {
// Initializes the OpenSAML library, loading default
configurations.
DefaultBootstrap.bootstrap();
// Gets a schema that can validate SAML 1.1, 2.0, and
all registered
// extensions.
Schema schema = SAMLSchemaBuilder.getSAML11Schema();
// get parser pool manager
BasicParserPool parserPoolManager = new
BasicParserPool();
parserPoolManager.setNamespaceAware(true);
parserPoolManager.setIgnoreElementContentWhitespace(true);
parserPoolManager.setSchema(schema);
InputStream in = new
ByteArrayInputStream(decodedSamlToken.getBytes("UTF-8"));
// parse input stream
Document document = parserPoolManager.parse(in);
Element metadataRoot = document.getDocumentElement();
javax.xml.namespace.QName qName = new
javax.xml.namespace.QName(
metadataRoot.getNamespaceURI(),
metadataRoot.getLocalName(),
metadataRoot.getPrefix());
// get an unmarshaller
Unmarshaller unmarshaller =
Configuration.getUnmarshallerFactory().getUnmarshaller(qName);
// unmarshall using the document root element
response = (Response)
unmarshaller.unmarshall(metadataRoot);
} catch (ConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XMLParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnmarshallingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
List<Assertion> assList = response.getAssertions();
// Assertion assertion = (Assertion)
response.getAssertions();
System.out.println("assertion length:" + assList.size());
assertion = assList.get(0);
Signature signature = assertion.getSignature();
File certificateFile = new
File("C:\\ginashare\\adfs_token_trust.cer");
// get the certificate from the file
CertificateFactory certificateFactory;
SignatureValidator signatureValidator = null;
X509EncodedKeySpec publicKeySpec = null;
InputStream certInputStream = null;
try {
certInputStream = new
FileInputStream(certificateFile);
certificateFactory =
CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)
certificateFactory
.generateCertificate(certInputStream);
// pull out the public key part of the certificate
into a
// KeySpec
publicKeySpec = new
X509EncodedKeySpec(certificate.getPublicKey()
.getEncoded());
// get KeyFactory object that creates key objects,
specifying
// RSA
} catch (CertificateException e1) {
// TODO Auto-generated catch block
System.out.println("CertificateException thrown");
e1.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance("RSA");
System.out.println("Security Provider: "
+
keyFactory.getProvider().toString());
// generate public key to validate signatures
PublicKey publicKey =
keyFactory.generatePublic(publicKeySpec);
// we have the public key
System.out.println("Public Key created");
// create credentials
BasicX509Credential publicCredential = new
BasicX509Credential();
// add public key value
publicCredential.setPublicKey(publicKey);
// create SignatureValidator
signatureValidator = new
SignatureValidator(publicCredential);
System.out.println("Algorithm:" +
publicKey.getAlgorithm());
System.out.println("Format:" +
publicKey.getFormat());
// no validation exception was thrown
System.out.println("Signature is valid.");
} catch (NoSuchAlgorithmException e) {
e.getClass().getSimpleName();
// TODO Auto-generated catch block
System.out.println("NoSuchAlgorithmException
thrown");
e.printStackTrace();
} catch (InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
System.out.println("Validating signature....");
signatureValidator.validate(signature);
System.out.println("Finished Validating signature");
} catch (Exception ve) {
System.out.println("Signature is NOT valid.");
System.out.println(ve.getStackTrace());
}
-----Original Message-----
From: mace-opensaml...@internet2.edu
[mailto:mace-opensaml...@internet2.edu] On Behalf Of Brent Putman
Sent: Thursday, April 28, 2011 11:24 AM
To: mace-open...@internet2.edu
Subject: Re: [OpenSAML] How to validate signing certificate of the SAML token
in the relaying party?
Until they change the app or your code gets copied around as an example or approach for some other app.
> My identity provider is Microsoft ADFS2.0 and my
> application is receiving SAML2.0 tokens from ADFS and I don't use any other
> third party product. I exported token signing certificate from ADFS and
> placed it in my application(SP). The singing certificate has an expiration
> date of one year. My worry is after one year what happens? I will keep work
> as normal or something will break?
That's up to you. And what happens if and when they change it?
You need to read what I provided as background. It's not optional if you're implementing SAML (or anything else involving keys for trust management).
-- Scott
The link that you previously sent me was very helpful. I got some idea about
how to handle expiration date of the certificate.
Thanks.
Gina
-----Original Message-----
From: mace-opensaml...@internet2.edu
[mailto:mace-opensaml...@internet2.edu] On Behalf Of Cantor, Scott
E.
Sent: Thursday, April 28, 2011 12:44 PM
To: mace-open...@internet2.edu
Subject: RE: [OpenSAML] How to validate signing certificate of the SAML token
in the relaying party?
On Apr 28 2011 12:40 -0400, from gc...@sdl.com (Gina Choi):
> The following is my initial code(definitely I will refactor it later) to
> decode the string(I don't know if it is a ADFS specific, the SAML token was
> encoded with Base64)
You may want to look over the parts of the standard that are relevant
to your use case. For one, "Bindings", section 3.5 (HTTP POST Binding)
clearly states "The HTTP POST binding defines a mechanism by which
SAML protocol messages may be transmitted within _the base64-encoded
content_ of an HTML form control.".
http://saml.xml.org/saml-specifications
- --
Michael Kjörling .. mic...@kjorling.se .. http://michael.kjorling.se
* ..... No bird soars too high if he soars with his own wings ..... *
* ENCRYPTED email preferred -- OpenPGP keys: 0x32D6B8C6, 0xBDE9ADA6 *
* ASCII Ribbon Campaign: Against HTML mail, proprietary attachments *
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iD8DBQFNupDydY+HSb3praYRAsIFAJ9orx3CFd+XMFXz6biDgO/JH8HFDQCgiKBO
CGWjzfouvS5t12fmlVQUfYQ=
=FSJU
-----END PGP SIGNATURE-----
<Subject>
<NameID>gchoi</NameID>
<SubjectConfirmation Method =
"urn:oasis:names:tc:SAML:2.0:cm:bearer">
<SubjectConfirmationData NotOnOrAfter =
"2011-04-12T18:37:00.243Z" Recipient
="https://wkensv0303.global.sdl.corp:8443/servletTestApp/testServlet"/>
</SubjectConfirmation>
</Subject>
<Conditions NotBefore = "2011-04-12T18:32:00.237Z" NotOnOrAfter =
"2011-04-12T19:32:00.237Z">
<AudienceRestriction>
<Audience>https://wkensv0303.global.sdl.corp:8443/servletTestApp</Audience>
</AudienceRestriction>
</Conditions>
<AttributeStatement>
Thanks.
Gina
That's backwards from accepted norms for bearer assertions, but a
condition is going to be an upper bound on subject confirmation anyway.
>I looked at document for
>Assertion protocols and it seems that NotOnOrAfter in the
>SubjectConfirmation
>is to restrict Subject data while the one in the Conditions tag is to
>restrict the Assertion token, but I wonder why do we need NotOnOrAfter in
>both places? Isn't one in the either place enough?
No, since they serve completely different functions.
-- Scott
Gina Choi
-----Original Message-----
From: mace-opensaml...@internet2.edu
[mailto:mace-opensaml...@internet2.edu] On Behalf Of Cantor, Scott
E.
Sent: Monday, May 02, 2011 12:37 PM
To: mace-open...@internet2.edu
I thought you said the confirmation window was the one that was ahead of
the other. That means it's as expected.
> By the way where are the 5 min and 1
>hour coming from? Is this implementation specific?
It's policy and profile specific (or should be).
> Timeframe of NotOnOrAfter
>in SubjectConfirmationData(5min) is much shorter than the one in the
>Conditions(60min).
It should be.
> Because of time off between identity server and relying
>party server can happen, I am thinking that verifying NotONOrAfter in the
>Conditions tag is realistic than the one in the SubjectConfirmation. Your
>advise would be appreciated.
Clock synchronication is a requirement in SAML and most security
protocols. Doing what you propose is wrong, and would be insecure.
-- Scott
>Clock synchronication is a requirement in SAML and most security
>protocols. Doing what you propose is wrong, and would be insecure.
1. How do I force clock synchronizations between two servers?
2. Could you recommend correct way of verifying NotOnOrAftr timestamp?
Thank you.
Gina Choi
-----Original Message-----
From: mace-opensaml...@internet2.edu
[mailto:mace-opensaml...@internet2.edu] On Behalf Of Cantor, Scott
E.
Sent: Monday, May 02, 2011 1:05 PM
To: mace-open...@internet2.edu
Subject: Re: [OpenSAML] Difference NotOnOrAfter in <SubjectConfirmationData>
and <Conditions>
>1. How do I force clock synchronizations between two servers?
By configuring all servers with a good time source. This is basic server
admin 101.
>2. Could you recommend correct way of verifying NotOnOrAftr timestamp?
Allow a configurable amount of clock skew of 3-5 minutes or so and then
apply that to the value you're comparing to in the conservative direction.
-- Scott
Thank you so much for your advice. I need to verify two more things before I
start on(hopefully doesn't bother you more on same question).
1. Do you recommend me verifying NotBefore timestamp in both
SubjectConfirmation and Conditions? I should, correct? Even one is enclosed
by the other(time range wise), but they have different meanings as you said.
2. Where can I get technical description about 5 min and 1 hour? I just want
to have a supportive document when later asked by others.
Thanks again. When you have a chance to come to Boston, please let me know. I
should take you out for lunch.:)
Gina Choi
-----Original Message-----
From: mace-opensaml...@internet2.edu
[mailto:mace-opensaml...@internet2.edu] On Behalf Of Cantor, Scott
E.
Sent: Monday, May 02, 2011 1:50 PM
To: mace-open...@internet2.edu
Subject: Re: [OpenSAML] Difference NotOnOrAfter in <SubjectConfirmationData>
and <Conditions>
You're obligated to. The validity of an assertion depends on both
generic/invariant processing rules and rules specific to profiles and the
context of use, such as when subject confirmation is involved because an
assertion is used for authentication.
>2. Where can I get technical description about 5 min and 1 hour? I just
>want
>to have a supportive document when later asked by others.
They're policy, nothing is going to absolutely dictate what the values
should be, particularly the assertion lifetime. But the profile neglects
to explicitly say the confirmation window should be short. That's either a
bug, or more likely a result of "everything is policy and risk management"
attitudes during drafting.
The Security Considerations document mentions it, but it's old text that
just mentions NotBefore and NonOnOrAfter and neglects to note which set
it's talking about. It's out of date.
I may propose an errata, probably to the definition of the "bearer"
confirmation method to catch a wider net.
>Thanks again. When you have a chance to come to Boston, please let me
>know. I
>should take you out for lunch.:)
I haven't been in awhile actually.
-- Scott