001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nelson Silva <nelson.silva@inevo.pt> 018 */ 019package org.nuxeo.ecm.platform.auth.saml.key; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.nuxeo.runtime.model.ComponentInstance; 024import org.nuxeo.runtime.model.DefaultComponent; 025import org.opensaml.common.SAMLRuntimeException; 026import org.opensaml.xml.security.CriteriaSet; 027import org.opensaml.xml.security.SecurityException; 028import org.opensaml.xml.security.credential.Credential; 029import org.opensaml.xml.security.credential.KeyStoreCredentialResolver; 030import org.opensaml.xml.security.criteria.EntityIDCriteria; 031 032import java.io.File; 033import java.io.FileInputStream; 034import java.io.IOException; 035import java.io.InputStream; 036import java.security.KeyStore; 037import java.security.KeyStoreException; 038import java.security.NoSuchAlgorithmException; 039import java.security.cert.CertificateException; 040import java.security.cert.X509Certificate; 041import java.util.Enumeration; 042import java.util.HashSet; 043import java.util.Set; 044 045/** 046 * An implementation of {@link KeyManager} that uses a JKS key store. 047 */ 048public class KeyManagerImpl extends DefaultComponent implements KeyManager { 049 050 private static final Log log = LogFactory.getLog(KeyManagerImpl.class); 051 052 private static final String KEYSTORE_TYPE = "JKS"; 053 054 KeyDescriptor config; 055 056 private KeyStore keyStore; 057 058 private KeyStoreCredentialResolver credentialResolver; 059 060 private Set<String> availableCredentials; 061 062 @Override 063 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 064 config = (KeyDescriptor) contribution; 065 setup(); 066 } 067 068 private void setup() { 069 if (config != null) { 070 try { 071 keyStore = getKeyStore(config.getKeystoreFilePath(), config.getKeystorePassword()); 072 } catch (SecurityException e) { 073 throw new RuntimeException(e); 074 } 075 credentialResolver = new KeyStoreCredentialResolver(keyStore, config.getPasswords()); 076 } else { 077 keyStore = null; 078 credentialResolver = null; 079 availableCredentials = null; 080 } 081 } 082 083 private KeyStore getKeyStore(String path, String password) throws SecurityException { 084 KeyStore ks; 085 try { 086 File rootKeystoreFile = new File(path); 087 if (!rootKeystoreFile.exists()) { 088 throw new SecurityException("Unable to find keyStore at " + new File(".").getAbsolutePath() 089 + File.separator + path); 090 } 091 InputStream keystoreIS = new FileInputStream(rootKeystoreFile); 092 ks = java.security.KeyStore.getInstance(KEYSTORE_TYPE); 093 ks.load(keystoreIS, password.toCharArray()); 094 } catch (KeyStoreException | IOException e) { 095 throw new SecurityException(e); 096 } catch (NoSuchAlgorithmException e) { 097 throw new SecurityException(e); 098 } catch (CertificateException e) { 099 throw new SecurityException(e); 100 } 101 return ks; 102 } 103 104 @Override 105 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 106 config = null; 107 setup(); 108 } 109 110 @Override 111 public Credential getCredential(String keyName) { 112 try { 113 CriteriaSet cs = new CriteriaSet(); 114 EntityIDCriteria criteria = new EntityIDCriteria(keyName); 115 cs.add(criteria); 116 return resolveSingle(cs); 117 } catch (org.opensaml.xml.security.SecurityException e) { 118 throw new SAMLRuntimeException("Can't obtain SP signing key", e); 119 } 120 } 121 122 @Override 123 public Set<String> getAvailableCredentials() { 124 if (availableCredentials != null) { 125 return availableCredentials; 126 } 127 try { 128 availableCredentials = new HashSet<>(); 129 Enumeration<String> aliases = keyStore.aliases(); 130 while (aliases.hasMoreElements()) { 131 availableCredentials.add(aliases.nextElement()); 132 } 133 return availableCredentials; 134 } catch (KeyStoreException e) { 135 throw new RuntimeException("Unable to load aliases from keyStore", e); 136 } 137 } 138 139 public X509Certificate getCertificate(String alias) { 140 if (alias == null || alias.length() == 0) { 141 return null; 142 } 143 try { 144 return (X509Certificate) keyStore.getCertificate(alias); 145 } catch (KeyStoreException e) { 146 log.error("Error loading certificate", e); 147 } 148 return null; 149 } 150 151 @Override 152 public Credential getSigningCredential() { 153 if (!hasCredentials() || config.getSigningKey() == null) { 154 return null; 155 } 156 return getCredential(config.getSigningKey()); 157 } 158 159 @Override 160 public Credential getEncryptionCredential() { 161 if (!hasCredentials() || config.getEncryptionKey() == null) { 162 return null; 163 } 164 return getCredential(config.getEncryptionKey()); 165 } 166 167 @Override 168 public Credential getTlsCredential() { 169 if (!hasCredentials() || config.getTlsKey() == null) { 170 return null; 171 } 172 return getCredential(config.getTlsKey()); 173 } 174 175 @Override 176 public Iterable<Credential> resolve(CriteriaSet criteria) throws org.opensaml.xml.security.SecurityException { 177 return credentialResolver.resolve(criteria); 178 } 179 180 @Override 181 public Credential resolveSingle(CriteriaSet criteria) throws SecurityException { 182 return credentialResolver.resolveSingle(criteria); 183 } 184 185 private boolean hasCredentials() { 186 return config != null && credentialResolver != null; 187 } 188}