001    /*
002     *   Licensed to the Apache Software Foundation (ASF) under one
003     *   or more contributor license agreements.  See the NOTICE file
004     *   distributed with this work for additional information
005     *   regarding copyright ownership.  The ASF licenses this file
006     *   to you under the Apache License, Version 2.0 (the
007     *   "License"); you may not use this file except in compliance
008     *   with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *   Unless required by applicable law or agreed to in writing,
013     *   software distributed under the License is distributed on an
014     *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *   KIND, either express or implied.  See the License for the
016     *   specific language governing permissions and limitations
017     *   under the License.
018     *
019     */
020    package org.apache.directory.server.core.security;
021    
022    
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.OutputStream;
026    import java.security.Key;
027    import java.security.KeyPair;
028    import java.security.KeyStoreException;
029    import java.security.KeyStoreSpi;
030    import java.security.NoSuchAlgorithmException;
031    import java.security.UnrecoverableKeyException;
032    import java.security.cert.Certificate;
033    import java.security.cert.CertificateException;
034    import java.security.cert.X509Certificate;
035    import java.util.Date;
036    import java.util.Enumeration;
037    
038    import org.apache.commons.lang.ArrayUtils;
039    import org.apache.commons.lang.NotImplementedException;
040    import org.apache.directory.server.constants.ServerDNConstants;
041    import org.apache.directory.server.core.CoreSession;
042    import org.apache.directory.server.core.DirectoryService;
043    import org.apache.directory.server.core.LdapPrincipal;
044    import org.apache.directory.server.i18n.I18n;
045    import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
046    import org.apache.directory.shared.ldap.entry.ServerEntry;
047    import org.apache.directory.shared.ldap.name.DN;
048    import org.apache.directory.shared.ldap.util.SingletonEnumeration;
049    import org.slf4j.Logger;
050    import org.slf4j.LoggerFactory;
051    
052    
053    /**
054     * A read only key store facility designed specifically for TLS/CA operations.
055     * It is only intended for accessing the 'apacheds' private/public key pairs
056     * as well as the self signed certificate.
057     *
058     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
059     * @version $Rev$, $Date$
060     */
061    public class CoreKeyStoreSpi extends KeyStoreSpi
062    {
063        private static final String APACHEDS_ALIAS = "apacheds";
064    
065        private static final Logger LOG = LoggerFactory.getLogger( CoreKeyStoreSpi.class );
066        
067        private DirectoryService directoryService;
068        
069    
070        /**
071         * Creates a new instance of LocalKeyStore.
072         */
073        public CoreKeyStoreSpi( DirectoryService directoryService )
074        {
075            LOG.debug( "Constructor called." );
076            this.directoryService = directoryService;
077        }
078    
079    
080        private ServerEntry getTlsEntry() throws Exception
081        {
082            DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN );
083            adminDn.normalize( directoryService.getSchemaManager().getNormalizerMapping() );
084            LdapPrincipal principal = new LdapPrincipal( adminDn, AuthenticationLevel.SIMPLE );
085            CoreSession session = directoryService.getSession( principal );
086            return session.lookup( adminDn );
087        }
088        
089        
090        /* (non-Javadoc)
091         * @see java.security.KeyStoreSpi#engineAliases()
092         */
093        @Override
094        public Enumeration<String> engineAliases()
095        {
096            LOG.debug( "engineAliases() called." );
097            return new SingletonEnumeration<String>( APACHEDS_ALIAS );
098        }
099    
100    
101        /* (non-Javadoc)
102         * @see java.security.KeyStoreSpi#engineContainsAlias(java.lang.String)
103         */
104        @Override
105        public boolean engineContainsAlias( String alias )
106        {
107            LOG.debug( "engineContainsAlias({}) called.", alias );
108            
109            if ( alias.equalsIgnoreCase( APACHEDS_ALIAS ) )
110            {
111                return true;
112            }
113            
114            return false;
115        }
116    
117    
118        /* (non-Javadoc)
119         * @see java.security.KeyStoreSpi#engineDeleteEntry(java.lang.String)
120         */
121        @Override
122        public void engineDeleteEntry( String alias ) throws KeyStoreException
123        {
124            LOG.debug( "engineDeleteEntry({}) called.", alias );
125            throw new UnsupportedOperationException();
126        }
127    
128    
129        /* (non-Javadoc)
130         * @see java.security.KeyStoreSpi#engineGetCertificate(java.lang.String)
131         */
132        @Override
133        public Certificate engineGetCertificate( String alias )
134        {
135            LOG.debug( "engineGetCertificate({}) called.", alias );
136            if ( alias.equalsIgnoreCase( APACHEDS_ALIAS ) )
137            {
138                try
139                {
140                    ServerEntry entry = getTlsEntry();
141                    return TlsKeyGenerator.getCertificate( entry );
142                }
143                catch ( Exception e )
144                {
145                    LOG.error( I18n.err( I18n.ERR_65 ), e );
146                }
147            }
148            
149            return null;
150        }
151    
152    
153        /* (non-Javadoc)
154         * @see java.security.KeyStoreSpi#engineGetCertificateAlias(java.security.cert.Certificate)
155         */
156        @Override
157        public String engineGetCertificateAlias( Certificate cert )
158        {
159            LOG.debug( "engineGetCertificateAlias({}) called.", cert );
160            
161            if ( cert instanceof X509Certificate )
162            {
163                LOG.debug( "Certificate in alias request is X.509 based." );
164                X509Certificate xcert = ( X509Certificate ) cert;
165                if ( xcert.getSubjectDN().toString().equals( TlsKeyGenerator.CERTIFICATE_PRINCIPAL_DN ) )
166                {
167                    return APACHEDS_ALIAS;
168                }
169            }
170            
171            try
172            {
173                ServerEntry entry = getTlsEntry();
174                if ( ArrayUtils.isEquals( cert.getEncoded(), entry.get( TlsKeyGenerator.USER_CERTIFICATE_AT ).getBytes() ) )
175                {
176                    return APACHEDS_ALIAS;
177                }
178            }
179            catch ( Exception e )
180            {
181                LOG.error( I18n.err( I18n.ERR_66 ), e );
182            }
183            
184            return null;
185        }
186    
187    
188        /* (non-Javadoc)
189         * @see java.security.KeyStoreSpi#engineGetCertificateChain(java.lang.String)
190         */
191        @Override
192        public Certificate[] engineGetCertificateChain( String alias )
193        {
194            LOG.debug( "engineGetCertificateChain({}) called.", alias );
195            try
196            {
197                ServerEntry entry = getTlsEntry();
198                LOG.debug( "Entry:\n{}", entry );
199                return new Certificate[] { TlsKeyGenerator.getCertificate( entry ) };
200            }
201            catch ( Exception e )
202            {
203                LOG.error( I18n.err( I18n.ERR_66 ), e );
204            }
205            
206            return new Certificate[0];
207        }
208    
209    
210        /* (non-Javadoc)
211         * @see java.security.KeyStoreSpi#engineGetCreationDate(java.lang.String)
212         */
213        @Override
214        public Date engineGetCreationDate( String alias )
215        {
216            LOG.debug( "engineGetCreationDate({}) called.", alias );
217            return new Date();
218        }
219    
220    
221        /* (non-Javadoc)
222         * @see java.security.KeyStoreSpi#engineGetKey(java.lang.String, char[])
223         */
224        @Override
225        public Key engineGetKey( String alias, char[] password ) throws NoSuchAlgorithmException, UnrecoverableKeyException
226        {
227            LOG.debug( "engineGetKey({}, {}) called.", alias, password );
228            
229            try
230            {
231                ServerEntry entry = getTlsEntry();
232                KeyPair keyPair = TlsKeyGenerator.getKeyPair( entry );
233                return keyPair.getPrivate();
234            }
235            catch ( Exception e )
236            {
237                LOG.error( I18n.err( I18n.ERR_68 ), e );
238            }
239            
240            return null;
241        }
242    
243    
244        /* (non-Javadoc)
245         * @see java.security.KeyStoreSpi#engineIsCertificateEntry(java.lang.String)
246         */
247        @Override
248        public boolean engineIsCertificateEntry( String alias )
249        {
250            LOG.debug( "engineIsCertificateEntry({}) called.", alias );
251            return false;
252        }
253    
254    
255        /* (non-Javadoc)
256         * @see java.security.KeyStoreSpi#engineIsKeyEntry(java.lang.String)
257         */
258        @Override
259        public boolean engineIsKeyEntry( String alias )
260        {
261            LOG.debug( "engineIsKeyEntry({}) called.", alias );
262            return true;
263        }
264    
265    
266        /* (non-Javadoc)
267         * @see java.security.KeyStoreSpi#engineLoad(java.io.InputStream, char[])
268         */
269        @Override
270        public void engineLoad( InputStream stream, char[] password ) throws IOException, NoSuchAlgorithmException,
271            CertificateException
272        {
273            LOG.debug( "engineLoad({}, {}) called.", stream, password );
274        }
275    
276    
277        /* (non-Javadoc)
278         * @see java.security.KeyStoreSpi#engineSetCertificateEntry(java.lang.String, java.security.cert.Certificate)
279         */
280        @Override
281        public void engineSetCertificateEntry( String alias, Certificate cert ) throws KeyStoreException
282        {
283            LOG.debug( "engineSetCertificateEntry({}, {}) called.", alias, cert );
284            throw new NotImplementedException();
285        }
286    
287    
288        /* (non-Javadoc)
289         * @see java.security.KeyStoreSpi#engineSetKeyEntry(java.lang.String, byte[], java.security.cert.Certificate[])
290         */
291        @Override
292        public void engineSetKeyEntry( String alias, byte[] key, Certificate[] chain ) throws KeyStoreException
293        {
294            LOG.debug( "engineSetKeyEntry({}, key, {}) called.", alias, chain );
295            throw new NotImplementedException();
296        }
297    
298    
299        /* (non-Javadoc)
300         * @see java.security.KeyStoreSpi#engineSetKeyEntry(java.lang.String, java.security.Key, char[], java.security.cert.Certificate[])
301         */
302        @Override
303        public void engineSetKeyEntry( String alias, Key key, char[] password, Certificate[] chain )
304            throws KeyStoreException
305        {
306            LOG.debug( "engineSetKeyEntry({}, key, {}, chain) called.", alias, new String( password ) );
307            throw new NotImplementedException();
308        }
309    
310    
311        /* (non-Javadoc)
312         * @see java.security.KeyStoreSpi#engineSize()
313         */
314        @Override
315        public int engineSize()
316        {
317            LOG.debug( "engineSize() called." );
318            return 1;
319        }
320    
321    
322        /* (non-Javadoc)
323         * @see java.security.KeyStoreSpi#engineStore(java.io.OutputStream, char[])
324         */
325        @Override
326        public void engineStore( OutputStream stream, char[] password ) throws IOException, NoSuchAlgorithmException,
327            CertificateException
328        {
329            LOG.debug( "engineStore(stream, {}) called.", new String( password ) );
330        }
331    }