/*
* Copyright 2005-2007 WSO2, Inc. (http://wso2.com)
*
* 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.wso2.carbon.identity.sso.saml.admin;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.X509V3CertificateGenerator;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.IdentityRegistryResources;
import org.wso2.carbon.identity.core.model.SAMLSSOServiceProviderDO;
import org.wso2.carbon.identity.core.persistence.IdentityPersistenceManager;
import org.wso2.carbon.identity.sso.saml.dto.SAMLSSOServiceProviderDTO;
import org.wso2.carbon.identity.sso.saml.dto.SAMLSSOServiceProviderInfoDTO;
import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.session.UserRegistry;
import org.wso2.carbon.utils.ServerConstants;
import org.wso2.carbon.utils.WSO2Constants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
/**
* This class is used for managing SAML SSO providers. Adding, retrieving and removing service
* providers are supported here.
* In addition to that logic for generating key pairs for tenants except for tenant 0, is included
* here.
*/
public class SAMLSSOConfigAdmin {
private static Log log = LogFactory.getLog(SAMLSSOConfigAdmin.class);
private UserRegistry registry;
public SAMLSSOConfigAdmin(Registry userRegistry) {
registry = (UserRegistry) userRegistry;
}
/**
* Add a new service provider
*
* @param serviceProviderDTO service Provider DTO
* @return true if successful, false otherwise
* @throws IdentityException if fails to load the identity persistence manager
*/
public boolean addRelyingPartyServiceProvider(SAMLSSOServiceProviderDTO serviceProviderDTO) throws IdentityException {
SAMLSSOServiceProviderDO serviceProviderDO = new SAMLSSOServiceProviderDO();
serviceProviderDO.setIssuer(serviceProviderDTO.getIssuer());
serviceProviderDO.setAssertionConsumerUrl(serviceProviderDTO.getAssertionConsumerUrl());
serviceProviderDO.setCertAlias(serviceProviderDTO.getCertAlias());
serviceProviderDO.setUseFullyQualifiedUsername(serviceProviderDTO.isUseFullyQualifiedUsername());
serviceProviderDO.setDoSingleLogout(serviceProviderDTO.isDoSingleLogout());
serviceProviderDO.setLogoutURL(serviceProviderDTO.getLogoutURL());
serviceProviderDO.setDoSignAssertions(serviceProviderDTO.isDoSignAssertions());
IdentityPersistenceManager persistenceManager = IdentityPersistenceManager
.getPersistanceManager();
try {
return persistenceManager.addServiceProvider(registry, serviceProviderDO);
} catch (IdentityException e) {
log.error("Error obtaining a registry for adding a new service provider", e);
throw new IdentityException("Error obtaining a registry for adding a new service provider", e);
}
}
/**
* Retrieve all the relying party service providers
*
* @return set of RP Service Providers + file path of pub. key of generated key pair
*/
public SAMLSSOServiceProviderInfoDTO getServiceProviders() throws IdentityException {
SAMLSSOServiceProviderDTO[] serviceProviders = null;
try {
IdentityPersistenceManager persistenceManager = IdentityPersistenceManager
.getPersistanceManager();
SAMLSSOServiceProviderDO[] providersSet = persistenceManager.
getServiceProviders(registry);
serviceProviders = new SAMLSSOServiceProviderDTO[providersSet.length];
for (int i = 0; i < providersSet.length; i++) {
SAMLSSOServiceProviderDO providerDO = providersSet[i];
SAMLSSOServiceProviderDTO providerDTO = new SAMLSSOServiceProviderDTO();
providerDTO.setIssuer(providerDO.getIssuer());
providerDTO.setAssertionConsumerUrl(providerDO.getAssertionConsumerUrl());
providerDTO.setCertAlias(providerDO.getCertAlias());
serviceProviders[i] = providerDTO;
}
} catch (IdentityException e) {
log.error("Error obtaining a registry intance for reading service provider list", e);
throw new IdentityException("Error obtaining a registry intance for reading service provider list", e);
}
SAMLSSOServiceProviderInfoDTO serviceProviderInfoDTO = new SAMLSSOServiceProviderInfoDTO();
serviceProviderInfoDTO.setServiceProviders(serviceProviders);
//if it is tenant zero
if(registry.getTenantId() == 0){
serviceProviderInfoDTO.setTenantZero(true);
}
return serviceProviderInfoDTO;
}
/**
* Remove an existing service provider.
*
* @param issuer issuer name
* @return true is successful
* @throws IdentityException
*/
public boolean removeServiceProvider(String issuer) throws IdentityException {
try {
IdentityPersistenceManager persistenceManager = IdentityPersistenceManager.getPersistanceManager();
return persistenceManager.removeServiceProvider(registry, issuer);
} catch (IdentityException e) {
log.error("Error removing a Service Provider");
throw new IdentityException("Error removing a Service Provider", e);
}
}
/**
* Method used for generating a key pair for each tenant, except for tenant 0
*
* @return File path of the public key
* @throws Exception
*/
private String generateKeyPair() throws Exception {
String domainName = SAMLSSOUtil.getRealmService().getTenantManager().getDomain(registry.getTenantId());
//load keystore
X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();
KeyStoreManager keyMan = KeyStoreManager.getInstance(null);
KeyStore keyStore = keyMan.getKeyStore("userSSO.jks");
//generate keypair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
v3CertGen.setSerialNumber(BigInteger.valueOf(new SecureRandom().nextInt()));
v3CertGen.setIssuerDN(new X509Principal("CN=" + domainName + ", OU=None, O=None L=None, C=None"));
v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30));
v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365 * 10)));
v3CertGen.setSubjectDN(new X509Principal("CN=" + domainName + ", OU=None, O=None L=None, C=None"));
v3CertGen.setPublicKey(keyPair.getPublic());
v3CertGen.setSignatureAlgorithm("MD5WithRSAEncryption");
X509Certificate PKCertificate = v3CertGen.generateX509Certificate(keyPair.getPrivate());
//Generate the password
SecureRandom random = new SecureRandom();
String randString = new BigInteger(130, random).toString(12);
String password = randString.substring(randString.length() - 10, randString.length());
//add private key to KS
keyStore.setKeyEntry(new Integer(registry.getTenantId()).toString(), keyPair.getPrivate(), password.toCharArray(),
new java.security.cert.Certificate[]{PKCertificate});
keyMan.updateKeyStore("userSSO.jks", keyStore);
//save pub. key to a file
String filePath = dumpPubCert(MessageContext.getCurrentMessageContext().getConfigurationContext()
, PKCertificate.getEncoded(), null);
//store the keyinfo in registry
Resource keyInfoResource = registry.newResource();
keyInfoResource.setContent(PKCertificate.getEncoded());
keyInfoResource.addProperty(IdentityRegistryResources.PROP_SAML_SSO_PUB_KEY_FILE_PATH, filePath);
keyInfoResource.addProperty(IdentityRegistryResources.PROP_SAML_SSO_GEN_KEY_PASS, password);
registry.put(IdentityRegistryResources.SAML_SSO_GEN_KEY, keyInfoResource);
keyInfoResource.discard();
//return the location of the pub. key
return filePath;
}
/**
* Dumping the generated pub. cert to a file
*
* @param configurationContext
* @param cert content of the certificate
* @param uuid UUID used for the file name
* @return file system location of the pub. cert
*/
private String dumpPubCert(ConfigurationContext configurationContext, byte[] cert, String uuid) {
Map fileResourcesMap = null;
String workdir = null;
File pubCert = null;
OutputStream outStream = null;
String filePath = null;
workdir = (String) configurationContext.getProperty(ServerConstants.WORK_DIR);
pubCert = new File(workdir + File.separator + "pub_certs");
if (uuid == null) {
uuid = String.valueOf(System.currentTimeMillis() + Math.random()) + ".cert";
}
if (!pubCert.exists()) {
pubCert.mkdirs();
}
filePath = workdir + File.separator + "pub_certs" + File.separator + uuid;
try {
outStream = new FileOutputStream(filePath);
outStream.write(cert);
outStream.flush();
outStream.close();
} catch (Exception e) {
// TODO:
e.printStackTrace();
return null;
}
fileResourcesMap = (Map) configurationContext.getProperty(WSO2Constants.FILE_RESOURCE_MAP);
if (fileResourcesMap == null) {
fileResourcesMap = new Hashtable();
configurationContext.setProperty(WSO2Constants.FILE_RESOURCE_MAP, fileResourcesMap);
}
fileResourcesMap.put(uuid, filePath);
return WSO2Constants.ContextPaths.DOWNLOAD_PATH + "?id=" + uuid;
}
/**
* Method used to get the file path location, if the cert is already generated.
*
* @return File path, if the certificate is already is generated, return null otherwise
* @throws Exception
*/
private String getPubKeyFilePath() throws Exception {
Resource r;
if (registry.resourceExists(IdentityRegistryResources.SAML_SSO_GEN_KEY)) {
r = registry.get(IdentityRegistryResources.SAML_SSO_GEN_KEY);
String filePath = r.getProperty(IdentityRegistryResources.PROP_SAML_SSO_PUB_KEY_FILE_PATH);
verifyCertExistence(filePath, (byte[]) r.getContent(),
MessageContext.getCurrentMessageContext().getConfigurationContext());
return filePath;
} else {
return null;
}
}
/**
* Check whether the certificate is availalble in the file system, if not dump it again.
*
* @param uuid UUID used in the file path
* @param cert binary content of the cert.
* @param configurationContext configuration context of the current message
*/
private void verifyCertExistence(String uuid, byte[] cert, ConfigurationContext configurationContext) {
uuid = uuid.substring("/filedownload?id=".length(), uuid.length());
String workDir = (String) configurationContext.getProperty(ServerConstants.WORK_DIR);
File pubCert = new File(workDir + File.separator + "pub_certs" + File.separator + uuid);
//if cert is still available then exit
if (pubCert.exists()) {
Map fileResourcesMap = (Map) configurationContext.getProperty(WSO2Constants.FILE_RESOURCE_MAP);
if (fileResourcesMap == null) {
fileResourcesMap = new Hashtable();
configurationContext.setProperty(WSO2Constants.FILE_RESOURCE_MAP, fileResourcesMap);
}
if (fileResourcesMap.get(uuid) == null) {
fileResourcesMap.put(uuid, pubCert);
}
return;
}
//otherwise dump the cert
dumpPubCert(configurationContext, cert, uuid);
}
}