/* * 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.adapters.saml.config.parsers; import org.jboss.logging.Logger; import org.keycloak.adapters.saml.DefaultSamlDeployment; import org.keycloak.adapters.saml.SamlDeployment; import org.keycloak.adapters.saml.config.Key; import org.keycloak.adapters.saml.config.KeycloakSamlAdapter; import org.keycloak.adapters.saml.config.SP; import org.keycloak.common.enums.SslRequired; import org.keycloak.common.util.PemUtils; import org.keycloak.saml.SignatureAlgorithm; import org.keycloak.saml.common.exceptions.ParsingException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.util.HashSet; import java.util.Set; import org.keycloak.adapters.cloned.HttpClientBuilder; import java.net.URI; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class DeploymentBuilder { protected static Logger log = Logger.getLogger(DeploymentBuilder.class); public SamlDeployment build(InputStream xml, ResourceLoader resourceLoader) throws ParsingException { DefaultSamlDeployment deployment = new DefaultSamlDeployment(); DefaultSamlDeployment.DefaultIDP idp = new DefaultSamlDeployment.DefaultIDP(); DefaultSamlDeployment.DefaultSingleSignOnService sso = new DefaultSamlDeployment.DefaultSingleSignOnService(); DefaultSamlDeployment.DefaultSingleLogoutService slo = new DefaultSamlDeployment.DefaultSingleLogoutService(); idp.setSingleSignOnService(sso); idp.setSingleLogoutService(slo); KeycloakSamlAdapter adapter = (KeycloakSamlAdapter)(new KeycloakSamlAdapterXMLParser().parse(xml)); SP sp = adapter.getSps().get(0); deployment.setConfigured(true); deployment.setEntityID(sp.getEntityID()); deployment.setForceAuthentication(sp.isForceAuthentication()); deployment.setIsPassive(sp.isIsPassive()); deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat()); deployment.setLogoutPage(sp.getLogoutPage()); deployment.setSignatureCanonicalizationMethod(sp.getIdp().getSignatureCanonicalizationMethod()); deployment.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256); if (sp.getIdp().getSignatureAlgorithm() != null) { deployment.setSignatureAlgorithm(SignatureAlgorithm.valueOf(sp.getIdp().getSignatureAlgorithm())); } if (sp.getPrincipalNameMapping() != null) { SamlDeployment.PrincipalNamePolicy policy = SamlDeployment.PrincipalNamePolicy.valueOf(sp.getPrincipalNameMapping().getPolicy()); deployment.setPrincipalNamePolicy(policy); deployment.setPrincipalAttributeName(sp.getPrincipalNameMapping().getAttributeName()); } deployment.setRoleAttributeNames(sp.getRoleAttributes()); if (sp.getRoleAttributes() == null) { Set<String> roles = new HashSet<>(); roles.add("Role"); deployment.setRoleAttributeNames(roles); } if (sp.getSslPolicy() != null) { SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy()); deployment.setSslRequired(ssl); } if (sp.getKeys() != null) { for (Key key : sp.getKeys()) { if (key.isSigning()) { PrivateKey privateKey = null; PublicKey publicKey = null; if (key.getKeystore() != null) { KeyStore keyStore = loadKeystore(resourceLoader, key); Certificate cert = null; try { log.debugf("Try to load key [%s]", key.getKeystore().getCertificateAlias()); cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias()); if(cert == null) { log.errorf("Key alias %s is not found into keystore", key.getKeystore().getCertificateAlias()); } privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray()); publicKey = cert.getPublicKey(); } catch (Exception e) { throw new RuntimeException(e); } } else { if (key.getPrivateKeyPem() == null) { throw new RuntimeException("SP signing key must have a PrivateKey defined"); } try { privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim()); if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) { throw new RuntimeException("Sp signing key must have a PublicKey or Certificate defined"); } publicKey = getPublicKeyFromPem(key); } catch (Exception e) { throw new RuntimeException(e); } } KeyPair keyPair = new KeyPair(publicKey, privateKey); deployment.setSigningKeyPair(keyPair); } if (key.isEncryption()) { if (key.getKeystore() != null) { KeyStore keyStore = loadKeystore(resourceLoader, key); try { PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray()); deployment.setDecryptionKey(privateKey); } catch (Exception e) { throw new RuntimeException(e); } } else { if (key.getPrivateKeyPem() == null) { throw new RuntimeException("SP signing key must have a PrivateKey defined"); } try { PrivateKey privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim()); deployment.setDecryptionKey(privateKey); } catch (Exception e) { throw new RuntimeException(e); } } } } } deployment.setIdp(idp); idp.setEntityID(sp.getIdp().getEntityID()); sso.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getRequestBinding())); sso.setRequestBindingUrl(sp.getIdp().getSingleSignOnService().getBindingUrl()); if (sp.getIdp().getSingleSignOnService().getResponseBinding() != null) { sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding())); } if (sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl() != null) { if (! sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl().endsWith("/saml")) { throw new RuntimeException("AssertionConsumerServiceUrl must end with \"/saml\"."); } sso.setAssertionConsumerServiceUrl(URI.create(sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl())); } sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest()); sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature()); sso.setValidateAssertionSignature(sp.getIdp().getSingleSignOnService().isValidateAssertionSignature()); slo.setSignRequest(sp.getIdp().getSingleLogoutService().isSignRequest()); slo.setSignResponse(sp.getIdp().getSingleLogoutService().isSignResponse()); slo.setValidateResponseSignature(sp.getIdp().getSingleLogoutService().isValidateResponseSignature()); slo.setValidateRequestSignature(sp.getIdp().getSingleLogoutService().isValidateRequestSignature()); slo.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getRequestBinding())); slo.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getResponseBinding())); if (slo.getRequestBinding() == SamlDeployment.Binding.POST) { slo.setRequestBindingUrl(sp.getIdp().getSingleLogoutService().getPostBindingUrl()); } else { slo.setRequestBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl()); } if (slo.getResponseBinding() == SamlDeployment.Binding.POST) { slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getPostBindingUrl()); } else { slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl()); } if (sp.getIdp().getKeys() != null) { for (Key key : sp.getIdp().getKeys()) { if (key.isSigning()) { processSigningKey(idp, key, resourceLoader); } } } idp.setClient(new HttpClientBuilder().build(sp.getIdp().getHttpClientConfig())); idp.refreshKeyLocatorConfiguration(); return deployment; } private void processSigningKey(DefaultSamlDeployment.DefaultIDP idp, Key key, ResourceLoader resourceLoader) throws RuntimeException { PublicKey publicKey; if (key.getKeystore() != null) { KeyStore keyStore = loadKeystore(resourceLoader, key); Certificate cert = null; try { cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias()); } catch (KeyStoreException e) { throw new RuntimeException(e); } publicKey = cert.getPublicKey(); } else { if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) { throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined"); } publicKey = getPublicKeyFromPem(key); } idp.addSignatureValidationKey(publicKey); } protected static PublicKey getPublicKeyFromPem(Key key) { PublicKey publicKey; if (key.getPublicKeyPem() != null) { publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim()); } else { Certificate cert = PemUtils.decodeCertificate(key.getCertificatePem().trim()); publicKey = cert.getPublicKey(); } return publicKey; } protected static KeyStore loadKeystore(ResourceLoader resourceLoader, Key key) { String type = key.getKeystore().getType(); if (type == null) type = "JKS"; KeyStore keyStore = null; try { keyStore = KeyStore.getInstance(type); } catch (KeyStoreException e) { throw new RuntimeException(e); } InputStream is = null; if (key.getKeystore().getFile() != null) { File fp = new File(key.getKeystore().getFile()); if (!fp.exists()) { } try { is = new FileInputStream(fp); } catch (FileNotFoundException e) { throw new RuntimeException("KeyStore " + key.getKeystore().getFile() + " does not exist"); } } else { is = resourceLoader.getResourceAsStream(key.getKeystore().getResource()); if (is == null) { throw new RuntimeException("KeyStore " + key.getKeystore().getResource() + " does not exist"); } } try { keyStore.load(is, key.getKeystore().getPassword().toCharArray()); } catch (Exception e) { throw new RuntimeException(e); } return keyStore; } }