Wednesday, February 22, 2012

Generating metadata with OpenSAML

OpenSAML can be used to generate metadata. As with reading, the framework is pretty straight forward in relation to the metadata XML.

This is an example for generating a SP metadata file

We start by creating the EntityDescriptor, setting the EntityId and building the SSO descriptor.

EntityDescriptor spEntityDescriptor = SAMLUtil.buildSAMLObjectWithDefaultName(EntityDescriptor.class);
spEntityDescriptor.setEntityID(entityID);
SPSSODescriptor spSSODescriptor = SAMLUtil.buildSAMLObjectWithDefaultName(SPSSODescriptor.class);

In the SSO descriptor we request how we want the communication signed and encrypted

spSSODescriptor.setWantAssertionsSigned(true); spSSODescriptor.setAuthnRequestsSigned(true);

And what certificates we want to use

X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
keyInfoGeneratorFactory.setEmitEntityCertificate(true);
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance();

  
KeyDescriptor encKeyDescriptor = SAMLUtil.buildSAMLObjectWithDefaultName(KeyDescriptor.class);

encKeyDescriptor.setUse(UsageType.ENCRYPTION); //Set usage

// Generating key info. The element will contain the public key. The key is used to by the IDP to encrypt data
try {
 encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(X509Credential));
} catch (SecurityException e) {
 log.error(e.getMessage(), e);
}

spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor);
  
KeyDescriptor signKeyDescriptor = SAMLUtil.buildSAMLObjectWithDefaultName(KeyDescriptor.class);

signKeyDescriptor.setUse(UsageType.SIGNING);  //Set usage

// Generating key info. The element will contain the public key. The key is used to by the IDP to verify signatures
try {
 signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(X509Credential));
} catch (SecurityException e) {
 log.error(e.getMessage(), e);
}

spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor);

The keyInfoGenerator uses a X509Credential to generate the key info object with the public key.


Setting what type of pseudonym federation we want with the IDP.

// Request transient pseudonym
NameIDFormat nameIDFormat = SAMLUtil.buildSAMLObjectWithDefaultName(NameIDFormat.class);
nameIDFormat.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:transient");
spSSODescriptor.getNameIDFormats().add(nameIDFormat);

Setting location of services

AssertionConsumerService assertionConsumerService = SAMLUtil.buildSAMLObjectWithDefaultName(AssertionConsumerService.class);
assertionConsumerService.setIndex(0);
assertionConsumerService.setBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);

// Setting address for our AssertionConsumerService
assertionConsumerService.setLocation(assertionConsumerServiceURL);
spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);

And finally we set SAML as supported protocol and generate the XML

spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);

spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);

DocumentBuilder builder;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
Marshaller out = Configuration.getMarshallerFactory().getMarshaller(spEntityDescriptor);
out.marshall(spEntityDescriptor, document);

Transformer transformer = TransformerFactory.newInstance().newTransformer();
StringWriter stringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(stringWriter);
DOMSource source = new DOMSource(document);
transformer.transform(source, streamResult);
stringWriter.close();
String metadataXML = stringWriter.toString();

40 comments:

  1. Hi,

    Thanks so much for this example. Would it be possible to obtain the source code for SAMLUtil?

    Thanks,
    Rachel Struthers

    ReplyDelete
  2. I think this is what you want http://mylifewithjava.blogspot.no/2011/04/convenience-methods-for-opensaml.html

    ReplyDelete
  3. Hi,

    Very neat series of examples for OpenSAML. Very much appreciated.

    What is the difference between uildSAMLObjectWithDefaultName and buildXMLObjectDefaultName? I have had a look at the convenience method link that you have provided, but could not figure out the difference and the necessity between them. It would be very helpful to have any suggestion.

    Thanks,
    Ripul

    ReplyDelete
    Replies
    1. There is no differance, it should be buildSAMLObjectWithDefaultName. I have now changes this in all posts affected

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi Stefan,

    Many thanks for your reply. I have been able to generating SP Metadata with your examples, but struggling to find a way to generate IdP Metadata. Any suggestion would be highly appreciated.

    Ripul

    ReplyDelete
    Replies
    1. I have never tried that, but it shouldn't be very much different from SP metadata. I would suggest finding an example IDP metadata file and try to recreate it with OpenSAML. Many of the XML elements in the metadata are represented by OpenSAML classes with the same name. What exactly are you struggling with?

      Delete
    2. Many thanks for your reply.

      Actually, I have been able to generate the metadata. I am struggling with signing the metadata. I have looked into some examples for signing a SAML Assertion, but a bit lost for signing metadata. Any help would be highly appreciated.

      Ripul

      Delete
    3. Hi,

      I have managed to resolve all problems. I did not know EntityDescriptor has a setSignature method. It solved all the problems :). I would be totally lost without your helpful tutorials since OpenSAML documentation is close to nothing. Many thanks for that.

      Delete
    4. Yes that is the one to use. How do you generate the signatures? I will soon come out with a post on how to sign elements. It's on my todo list. Yes OpenSAML is not very well documented.

      Delete
    5. I used the tutorial from the following location which shows how to sign a SAML Assertion:

      http://narendrakadali.wordpress.com/2011/06/05/sign-assertion-using-opensaml/

      I had problems since I was not aware that the EntityDescriptor had setSignature method. So initially, I used the common XML Signing method which was very lengthy. Once I came to know about the setSignature method the code was neat and was just trivial.

      Many thanks.

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Thank you so much for this article. My onlu question is when you say keyInfoGenerator.generate(X509Credential),how do you generate this X509Credential? Do you buy a certificate from some CA?

    ReplyDelete
  8. any pointers for creating X509 Credential for a SP?

    ReplyDelete
    Replies
    1. Usually you don't buy a certificate from a CA for this purpose. You can generate one with OpenSSL.

      Delete
  9. how to generate the certificate from openSSL . I know we can generate it with a private key .
    1)I want to knw how to provide the issuing entity ID/alias ?
    2)And are the keystore password and keystore private key password same if different how to generate them also with the certificate

    ReplyDelete
    Replies
    1. try using keytool http://www.dotkam.com/2008/04/22/creating-public-and-private-certificateskeys/

      Delete
    2. thanks for the link . Could you provide me link or tutorial of how to configure the generate certificate/keystore to the CredentialResolver in opensaml . I want to implement my own class by extending that inferface CredentialResolver.

      Delete
    3. I'm not sure what you want but I think you whant to use the keystore in the link as a credential in OpenSAML. In that case I have everything you need. http://mylifewithjava.blogspot.no/2011/03/getting-credentials-in-opensaml.html

      Delete
    4. Ok. Let me explain you my problem. Currently the saml is using a JKS implemention for signing the saml assertion . its using basically a Base64 encoded value as the keystore and has a public and private keys . So we are using the opensaml KeyStoreCredentialResolver class for it to get the resolver. Now the problem is I want change the keystore from the JKS to openssl. Thank you for the link above but its also using the keyStoreCredentialsResolver to generate the credential. I cannot use it as it will expect the entityID as a parameter in password MAP . PLease HELP!!!

      Delete
    5. Change to openssl? What are you generating with openssl? p12? Why do you change?

      Delete
    6. Yes I need to modify the code FROM JKS to use a openssl pkcs8 file. And I generate a .PEM and then to use it with java I generate a PKCS8 file using openssl .

      Heres a code that i using to generate teh signing credentials :


      RandomAccessFile rafObj = new RandomAccessFile("D:\\saml_certifications\\sample.pkcs8", "r");
      byte[] buf = new byte[(int)rafObj.length()];
      rafObj.readFully(buf);
      rafObj.close();
      PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(buf);
      KeyFactory kf = KeyFactory.getInstance("RSA");
      PrivateKey privKey = kf.generatePrivate(kspec);

      credential = new BasicX509Credential();
      credential.setUsageType(UsageType.SIGNING);
      credential.setPrivateKey(privKey);
      return credential

      with the code above i am able to sign the but it failing to set the public key .

      Delete
    7. How about this then, I found some code in stackoverflow, http://stackoverflow.com/questions/2654949/how-to-read-a-password-encrypted-key-with-java

      KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
      // First get the private key
      RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) rsaKeyFac.generatePrivate(pkcs8KeySpec);
      // Now derive the RSA public key from the private key
      RSAPublicKeySpec rsaPubKeySpec = new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv.getPublicExponent());
      RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyFac.generatePublic(rsaPubKeySpec);

      credential = new BasicX509Credential();
      credential.setUsageType(UsageType.SIGNING);
      credential.setPrivateKey(rsaPriv);
      credential.setPublicKey(rsaPubKey);

      return credential

      Delete
    8. hey stefan, It works great !!! but the generate AuthnRequest xml is missing the attribute . how can i add it.

      Delete
    9. This comment has been removed by the author.

      Delete
    10. sorry my mistake i was referrring to KeyInfo with x506Certificate. I really didnt want to put all that code here in comment and spoil article. So i created a issue in StackOverflow :

      http://stackoverflow.com/questions/16399945/how-to-sign-an-saml-2-0-assertion-using-the-openssl-pcks8-file

      Please check it .

      Delete
    11. Good call, I will have a look tomorrow

      Delete
  10. I have generated signed SAML1.1 response using keystore. Now I got the metadata from SP, how to use this metadata in my Java code. Please help me ...

    We have to configure it on my local server or we have to write code for this. what is use of this metadata please suggest.

    ReplyDelete
  11. I am trying to use Generating metadata with OpenSAML to generate metadata to a given entity Id..Is it possible to share a code base for SAMLUtil class to me?

    ReplyDelete
    Replies
    1. You can find the methods here, http://mylifewithjava.blogspot.no/2011/04/convenience-methods-for-opensaml.html

      Delete
  12. hey Stefan, could the above be simplified more ?

    KeyDescriptor signKeyDescriptor =SAMLUtil.buildSAMLObjectWithDefaultName(KeyDescriptor.class);
    signKeyDescriptor.setUse(UsageType.SIGNING);
    X509Certificate x509Certificate = null;
    KeyInfo keyinfo = SAMLUtil.buildSAMLObjectWithDefaultName(KeyInfo.class);
    KeyInfoHelper.addCertificate(keyInfo, x509Certificate);
    signKeyDescriptor.setKeyInfo(keyInfo);

    ReplyDelete
  13. This comment has been removed by the author.

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
  15. Hi Stefen,

    I'm completely new to SAML2.0. I got a requirement to implement SP for one of our company product. I bought your book and is really helpful. I'm still confused with few questions. Can you please clarify my questions?
    I'm trying out service provider implementation on my local. Let us assume I have SP metadata that has my localhost url references for assertion consumer service.
    1. How can I test sso login with some test IdP account that runs on
    https?
    2. Do I need to exchange my localhost SP metadata with IdP? Can I
    do that at all? Is that recommended?
    3. How can I test OpenSAML implementation end to end in http
    environment even if the IdP runs on https?

    Basically I have to make sure sso work as expected before I move to development and then to production. Also our development environment does not run on https. I am really confused when it comes to testing part.

    ReplyDelete
    Replies
    1. Just for clarification, I have no control on IdP side. All I can request from IdP side is their metadata (both test and prod accounts).

      Delete
    2. To test you basically configure the SP and the IDP with each others metadata and then start a authentication from the SP. How this is done depends on the IDP.

      Yes you typically exchange the metadata with the IDP.

      If the IDP only accepts https and you con not control it then you will need use https for the IDP endpoints. However you can of course use http for you endpoints.

      Delete