001/* 002 * (C) Copyright 2011 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 * Wojciech Sulejman 018 */ 019package org.nuxeo.ecm.platform.signature.core.user; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.security.KeyStore; 024import java.security.cert.X509Certificate; 025import java.util.HashMap; 026import java.util.Map; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.nuxeo.common.utils.Base64; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.directory.DirectoryException; 033import org.nuxeo.ecm.directory.Session; 034import org.nuxeo.ecm.directory.api.DirectoryService; 035import org.nuxeo.ecm.platform.signature.api.exception.CertException; 036import org.nuxeo.ecm.platform.signature.api.pki.CertService; 037import org.nuxeo.ecm.platform.signature.api.pki.RootService; 038import org.nuxeo.ecm.platform.signature.api.user.AliasType; 039import org.nuxeo.ecm.platform.signature.api.user.AliasWrapper; 040import org.nuxeo.ecm.platform.signature.api.user.CNField; 041import org.nuxeo.ecm.platform.signature.api.user.CUserService; 042import org.nuxeo.ecm.platform.signature.api.user.UserInfo; 043import org.nuxeo.runtime.api.Framework; 044import org.nuxeo.runtime.model.ComponentInstance; 045import org.nuxeo.runtime.model.DefaultComponent; 046 047/** 048 * Base implementation of the user certificate service. 049 * 050 * @author <a href="mailto:ws@nuxeo.com">Wojciech Sulejman</a> 051 */ 052public class CUserServiceImpl extends DefaultComponent implements CUserService { 053 054 private static final Log LOG = LogFactory.getLog(CUserServiceImpl.class); 055 056 private static final String CERTIFICATE_DIRECTORY_NAME = "certificate"; 057 058 protected RootService rootService; 059 060 protected CertService certService; 061 062 /** 063 * Configurable country code 064 */ 065 protected String countryCode; 066 067 /** 068 * Configurable organization name 069 */ 070 protected String organization; 071 072 /** 073 * Configurable organizational unit name 074 */ 075 protected String organizationalUnit; 076 077 @Override 078 public UserInfo getUserInfo(DocumentModel userModel) throws CertException { 079 UserInfo userInfo = null; 080 String userID = (String) userModel.getPropertyValue("user:username"); 081 String firstName = (String) userModel.getPropertyValue("user:firstName"); 082 String lastName = (String) userModel.getPropertyValue("user:lastName"); 083 String email = (String) userModel.getPropertyValue("user:email"); 084 085 Map<CNField, String> userFields = new HashMap<CNField, String>(); 086 087 userFields.put(CNField.C, countryCode); 088 userFields.put(CNField.O, organization); 089 userFields.put(CNField.OU, organizationalUnit); 090 091 userFields.put(CNField.CN, firstName + " " + lastName); 092 userFields.put(CNField.Email, email); 093 userFields.put(CNField.UserID, userID); 094 userInfo = new UserInfo(userFields); 095 return userInfo; 096 } 097 098 @Override 099 public KeyStore getUserKeystore(String userID, String userKeystorePassword) throws CertException { 100 String keystore64Encoded = Framework.doPrivileged(() -> { 101 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 102 DocumentModel entry = session.getEntry(userID); 103 if (entry != null) { 104 return (String) entry.getPropertyValue("cert:keystore"); 105 } else { 106 throw new CertException("No directory entry for " + userID); 107 } 108 } 109 }); 110 byte[] keystoreBytes = Base64.decode(keystore64Encoded); 111 ByteArrayInputStream byteIS = new ByteArrayInputStream(keystoreBytes); 112 return getCertService().getKeyStore(byteIS, userKeystorePassword); 113 } 114 115 @Override 116 public DocumentModel createCertificate(DocumentModel user, String userKeyPassword) throws CertException { 117 return Framework.doPrivileged(() -> { 118 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 119 String userKeystorePassword = userKeyPassword; 120 DocumentModel certificate = null; 121 122 // create an entry in the directory 123 String userID = (String) user.getPropertyValue("user:username"); 124 125 // make sure that no certificates are associated with the 126 // current userid 127 boolean certificateExists = session.hasEntry(userID); 128 if (certificateExists) { 129 throw new CertException(userID + " already has a certificate"); 130 } 131 132 LOG.info("Starting certificate generation for: " + userID); 133 Map<String, Object> map = new HashMap<String, Object>(); 134 map.put("userid", userID); 135 136 // add a keystore to a directory entry 137 KeyStore keystore = getCertService().initializeUser(getUserInfo(user), userKeyPassword); 138 ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); 139 getCertService().storeCertificate(keystore, byteOS, userKeystorePassword); 140 String keystore64Encoded = Base64.encodeBytes(byteOS.toByteArray()); 141 map.put("keystore", keystore64Encoded); 142 map.put("certificate", getUserCertInfo(keystore, user)); 143 map.put("keypassword", userKeyPassword); 144 certificate = session.createEntry(map); 145 return certificate; 146 } catch (DirectoryException e) { 147 LOG.error(e); 148 throw new CertException(e); 149 } 150 }); 151 } 152 153 protected static DirectoryService getDirectoryService() { 154 return Framework.getService(DirectoryService.class); 155 } 156 157 @Override 158 public String getUserCertInfo(DocumentModel user, String userKeyPassword) throws CertException { 159 String userKeystorePassword = userKeyPassword; 160 String userID = (String) user.getPropertyValue("user:username"); 161 KeyStore keystore = getUserKeystore(userID, userKeystorePassword); 162 return getUserCertInfo(keystore, user); 163 } 164 165 private String getUserCertInfo(KeyStore keystore, DocumentModel user) throws CertException { 166 String userCertInfo = null; 167 if (null != keystore) { 168 String userID = (String) user.getPropertyValue("user:username"); 169 AliasWrapper alias = new AliasWrapper(userID); 170 X509Certificate certificate = getCertService().getCertificate(keystore, alias.getId(AliasType.CERT)); 171 userCertInfo = certificate.getSubjectDN() + " valid till: " + certificate.getNotAfter(); 172 } 173 return userCertInfo; 174 } 175 176 @Override 177 public DocumentModel getCertificate(String userID) { 178 return Framework.doPrivileged(() -> { 179 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 180 DocumentModel certificate = session.getEntry(userID); 181 return certificate; 182 } 183 }); 184 } 185 186 @Override 187 public byte[] getRootCertificateData() { 188 byte[] certificateData = getRootService().getRootPublicCertificate(); 189 return certificateData; 190 } 191 192 @SuppressWarnings("boxing") 193 @Override 194 public boolean hasCertificate(String userID) throws CertException { 195 return Framework.doPrivileged(() -> { 196 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 197 return session.getEntry(userID) != null; 198 } 199 }); 200 } 201 202 @Override 203 public void deleteCertificate(String userID) throws CertException { 204 Framework.doPrivileged(() -> { 205 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 206 DocumentModel certEntry = session.getEntry(userID); 207 session.deleteEntry(certEntry); 208 assert (null == session.getEntry(userID)); 209 } 210 }); 211 } 212 213 @Override 214 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 215 if (contribution instanceof CUserDescriptor) { 216 CUserDescriptor desc = (CUserDescriptor) contribution; 217 countryCode = desc.getCountryCode(); 218 organization = desc.getOrganization(); 219 organizationalUnit = desc.getOrganizationalUnit(); 220 } 221 } 222 223 protected CertService getCertService() { 224 if (certService == null) { 225 certService = Framework.getService(CertService.class); 226 } 227 return certService; 228 } 229 230 protected RootService getRootService() { 231 if (rootService == null) { 232 rootService = Framework.getService(RootService.class); 233 } 234 return rootService; 235 } 236}