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
021package org.apache.directory.server.ldap.replication;
022
023
024import java.io.ByteArrayInputStream;
025import java.security.KeyStore;
026import java.security.cert.CertificateException;
027import java.security.cert.X509Certificate;
028import java.util.Map;
029
030import javax.net.ssl.TrustManager;
031import javax.net.ssl.TrustManagerFactory;
032import javax.net.ssl.X509TrustManager;
033
034import org.bouncycastle.jce.provider.X509CertParser;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038
039/**
040 * A X509TrustManager implementation used by the replication subsystem.
041 * This implementation doesn't require the certificates to be stored in a file, instead
042 * it parses the given certificates of replica peers using Bouncycastle's X509CertParser 
043 * and stores them in the in-memory KeyStore.
044 * 
045 * The SunX509 TrustManagerFactory is then initialized using this KeyStore and the
046 * resulting X509TrustManager present in this factory's TrustManagers will be used
047 * internally to perform the certificate verification 
048 *
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public final class ReplicationTrustManager implements X509TrustManager
052{
053    /** A logger for this class */
054    private static final Logger LOG = LoggerFactory.getLogger( ReplicationTrustManager.class );
055
056    /** the internal trust manager used for verifying the certificates */
057    private static X509TrustManager trustManager = null;
058
059    /** the in-memory keystore */
060    private static KeyStore ks;
061
062    /** the X509 certificate parser */
063    private static X509CertParser parser = new X509CertParser();
064
065    /** the singleton instance of this trust manager */
066    private static final ReplicationTrustManager INSTANCE = new ReplicationTrustManager();
067
068    static
069    {
070        try
071        {
072            ks = KeyStore.getInstance( KeyStore.getDefaultType() );
073        }
074        catch ( Exception e )
075        {
076            LOG.error( "failed to initialize the keystore and X509 trustmanager", e );
077            throw new RuntimeException( e );
078        }
079    }
080
081    /**
082     * Creates a instance of ReplicationTrustManager
083     */
084    private ReplicationTrustManager()
085    {
086        try
087        {
088            ks.load( null, null ); // initiate with null stream and password, this keystore resides in-memory only
089
090            TrustManagerFactory tmFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
091            tmFactory.init( ks );
092
093            TrustManager[] trustManagers = tmFactory.getTrustManagers();
094
095            for ( int i = 0; i < trustManagers.length; i++ )
096            {
097                if ( trustManagers[i] instanceof X509TrustManager )
098                {
099                    trustManager = ( X509TrustManager ) trustManagers[i];
100                    LOG.debug( "found X509TrustManager {}", trustManager );
101                    break;
102                }
103            }
104        }
105        catch ( Exception e )
106        {
107            LOG.error( "failed to initialize the keystore and X509 trustmanager", e );
108            throw new RuntimeException( e );
109        }
110    }
111
112
113    /**
114     * loads the given map of [alias-name, certificate-data] entries into the keystore
115     * to be used by the trust manager
116     *
117     * @param aliasCertMap the map of [alias-name, certificate-data] entries
118     * @throws Exception in case of any issues related to certificate data parsing
119     */
120    public static void addCertificates( Map<String, byte[]> aliasCertMap ) throws Exception
121    {
122        for ( Map.Entry<String, byte[]> entry : aliasCertMap.entrySet() )
123        {
124            addCertificate( entry.getKey(), entry.getValue() );
125        }
126    }
127
128
129    /**
130     * stores the given certificate into the keystore with the given alias name
131     * 
132     * @param certAlias the alias name to be used for this certificate
133     * @param certificate the X509 certificate data
134     * @throws Exception in case of any issues related to certificate data parsing
135     */
136    public static void addCertificate( String certAlias, byte[] certificate ) throws Exception
137    {
138        try
139        {
140            parser.engineInit( new ByteArrayInputStream( certificate ) );
141
142            X509Certificate cert = ( X509Certificate ) parser.engineRead();
143
144            ks.setCertificateEntry( certAlias, cert );
145        }
146        catch ( Exception ex )
147        {
148            LOG.warn( "failed to load the certificate associated with the alias {}", certAlias, ex );
149            throw ex;
150        }
151    }
152
153
154    /**
155     * returns the singleton instance of ReplicationTrustManager, note that this
156     * return instance can only be used after calling the {@link #addCertificates(Map)} method 
157     * 
158     * @return the instance of the ReplicationTrustManager
159     */
160    public static ReplicationTrustManager getInstance()
161    {
162        return INSTANCE;
163    }
164
165
166    /**
167     * {@inheritDoc}
168     */
169    public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException
170    {
171        trustManager.checkClientTrusted( chain, authType );
172    }
173
174
175    /**
176     * {@inheritDoc}
177     */
178    public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException
179    {
180        trustManager.checkServerTrusted( chain, authType );
181    }
182
183
184    /**
185     * {@inheritDoc}
186     */
187    public X509Certificate[] getAcceptedIssuers()
188    {
189        return trustManager.getAcceptedIssuers();
190    }
191}