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.ByteArrayInputStream;
024 import java.io.InputStream;
025 import java.math.BigInteger;
026 import java.security.KeyFactory;
027 import java.security.KeyPair;
028 import java.security.KeyPairGenerator;
029 import java.security.NoSuchAlgorithmException;
030 import java.security.PrivateKey;
031 import java.security.PublicKey;
032 import java.security.Security;
033 import java.security.cert.CertificateException;
034 import java.security.cert.CertificateFactory;
035 import java.security.cert.X509Certificate;
036 import java.security.spec.EncodedKeySpec;
037 import java.security.spec.InvalidKeySpecException;
038 import java.security.spec.PKCS8EncodedKeySpec;
039 import java.security.spec.X509EncodedKeySpec;
040 import java.util.Date;
041
042 import javax.security.auth.x500.X500Principal;
043
044 import org.apache.directory.server.i18n.I18n;
045 import org.apache.directory.shared.ldap.constants.SchemaConstants;
046 import org.apache.directory.shared.ldap.entry.EntryAttribute;
047 import org.apache.directory.shared.ldap.entry.ServerEntry;
048 import org.apache.directory.shared.ldap.exception.LdapException;
049 import org.bouncycastle.jce.provider.BouncyCastleProvider;
050 import org.bouncycastle.x509.X509V1CertificateGenerator;
051 import org.slf4j.Logger;
052 import org.slf4j.LoggerFactory;
053
054
055 /**
056 * Generates the default RSA key pair for the server.
057 *
058 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
059 * @version $Rev$, $Date$
060 */
061 public class TlsKeyGenerator
062 {
063 private static final Logger LOG = LoggerFactory.getLogger( TlsKeyGenerator.class );
064
065 public static final String TLS_KEY_INFO_OC = "tlsKeyInfo";
066 public static final String PRIVATE_KEY_AT = "privateKey";
067 public static final String PUBLIC_KEY_AT = "publicKey";
068 public static final String KEY_ALGORITHM_AT = "keyAlgorithm";
069 public static final String PRIVATE_KEY_FORMAT_AT = "privateKeyFormat";
070 public static final String PUBLIC_KEY_FORMAT_AT = "publicKeyFormat";
071 public static final String USER_CERTIFICATE_AT = "userCertificate";
072
073 public static final String CERTIFICATE_PRINCIPAL_DN =
074 "CN=ApacheDS, OU=Directory, O=ASF, C=US";
075 private static final String ALGORITHM = "RSA";
076
077 /*
078 * Eventually we have to make several of these parameters configurable,
079 * however note to pass export restrictions we must use a key size of
080 * 512 or less here as the default. Users can configure this setting
081 * later based on their own legal situations. This is required to
082 * classify ApacheDS in the ECCN 5D002 category. Please see the following
083 * page for more information:
084 *
085 * http://www.apache.org/dev/crypto.html
086 *
087 * Also ApacheDS must be classified on the following page:
088 *
089 * http://www.apache.org/licenses/exports
090 */
091 private static final int KEY_SIZE = 512;
092 private static final long YEAR_MILLIS = 365L * 24L * 3600L * 1000L;
093
094
095 static
096 {
097 Security.addProvider( new BouncyCastleProvider() );
098 }
099
100
101 /**
102 * Gets the certificate associated with the self signed TLS private/public
103 * key pair.
104 *
105 * @param entry the TLS key/cert entry
106 * @return the X509 certificate associated with that entry
107 * @throws LdapException if there are problems accessing or decoding
108 */
109 public static X509Certificate getCertificate( ServerEntry entry ) throws LdapException
110 {
111 X509Certificate cert = null;
112 CertificateFactory certFactory = null;
113
114 try
115 {
116 certFactory = CertificateFactory.getInstance( "X.509", "BC" );
117 }
118 catch ( Exception e )
119 {
120 LdapException ne = new LdapException( I18n.err( I18n.ERR_286 ) );
121 ne.initCause( e );
122 throw ne;
123 }
124
125 byte[] certBytes = entry.get( USER_CERTIFICATE_AT ).getBytes();
126 InputStream in = new ByteArrayInputStream( certBytes );
127
128 try
129 {
130 cert = ( X509Certificate ) certFactory.generateCertificate( in );
131 }
132 catch ( CertificateException e )
133 {
134 LdapException ne = new LdapException( I18n.err( I18n.ERR_287 ) );
135 ne.initCause( e );
136 throw ne;
137 }
138
139 return cert;
140 }
141
142
143 /**
144 * Extracts the public private key pair from the tlsKeyInfo entry.
145 *
146 * @param entry an entry of the tlsKeyInfo objectClass
147 * @return the private and public key pair
148 * @throws LdapException if there are format or access issues
149 */
150 public static KeyPair getKeyPair( ServerEntry entry ) throws LdapException
151 {
152 PublicKey publicKey = null;
153 PrivateKey privateKey = null;
154
155 KeyFactory keyFactory = null;
156 try
157 {
158 keyFactory = KeyFactory.getInstance( ALGORITHM );
159 }
160 catch ( Exception e )
161 {
162 LdapException ne = new LdapException( I18n.err( I18n.ERR_288, ALGORITHM ) );
163 ne.initCause( e );
164 throw ne;
165 }
166
167 EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec( entry.get( PRIVATE_KEY_AT ).getBytes() );
168 try
169 {
170 privateKey = keyFactory.generatePrivate( privateKeySpec );
171 }
172 catch ( Exception e )
173 {
174 LdapException ne = new LdapException( I18n.err( I18n.ERR_289 ) );
175 ne.initCause( e );
176 throw ne;
177 }
178
179 EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( entry.get( PUBLIC_KEY_AT ).getBytes() );
180 try
181 {
182 publicKey = keyFactory.generatePublic( publicKeySpec );
183 }
184 catch ( InvalidKeySpecException e )
185 {
186 LdapException ne = new LdapException( I18n.err( I18n.ERR_290 ) );
187 ne.initCause( e );
188 throw ne;
189 }
190
191 return new KeyPair( publicKey, privateKey );
192 }
193
194
195 /**
196 * Adds a private key pair along with a self signed certificate to an
197 * entry making sure it contains the objectClasses and attributes needed
198 * to support the additions. This function is intended for creating a TLS
199 * key value pair and self signed certificate for use by the server to
200 * authenticate itself during SSL handshakes in the course of establishing
201 * an LDAPS connection or a secure LDAP connection using StartTLS. Usually
202 * this information is added to the administrator user's entry so the
203 * administrator (effectively the server) can manage these security
204 * concerns.
205 *
206 * @param entry the entry to add security attributes to
207 * @throws LdapException on problems generating the content in the entry
208 */
209 public static void addKeyPair( ServerEntry entry ) throws LdapException
210 {
211 EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
212
213 if ( objectClass == null )
214 {
215 entry.put( SchemaConstants.OBJECT_CLASS_AT, TLS_KEY_INFO_OC, SchemaConstants.INET_ORG_PERSON_OC );
216 }
217 else
218 {
219 objectClass.add( TLS_KEY_INFO_OC, SchemaConstants.INET_ORG_PERSON_OC );
220 }
221
222 KeyPairGenerator generator = null;
223 try
224 {
225 generator = KeyPairGenerator.getInstance( ALGORITHM );
226 }
227 catch ( NoSuchAlgorithmException e )
228 {
229 LdapException ne = new LdapException( I18n.err( I18n.ERR_291 ) );
230 ne.initCause( e );
231 throw ne;
232 }
233
234 generator.initialize( KEY_SIZE );
235 KeyPair keypair = generator.genKeyPair();
236 entry.put( KEY_ALGORITHM_AT, ALGORITHM );
237
238 // Generate the private key attributes
239 PrivateKey privateKey = keypair.getPrivate();
240 entry.put( PRIVATE_KEY_AT, privateKey.getEncoded() );
241 entry.put( PRIVATE_KEY_FORMAT_AT, privateKey.getFormat() );
242 LOG.debug( "PrivateKey: {}", privateKey );
243
244 PublicKey publicKey = keypair.getPublic();
245 entry.put( PUBLIC_KEY_AT, publicKey.getEncoded() );
246 entry.put( PUBLIC_KEY_FORMAT_AT, publicKey.getFormat() );
247 LOG.debug( "PublicKey: {}", publicKey );
248
249 // Generate the self-signed certificate
250 Date startDate = new Date();
251 Date expiryDate = new Date( System.currentTimeMillis() + YEAR_MILLIS );
252 BigInteger serialNumber = BigInteger.valueOf( System.currentTimeMillis() );
253
254 X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
255 X500Principal dnName = new X500Principal( CERTIFICATE_PRINCIPAL_DN );
256
257 certGen.setSerialNumber( serialNumber );
258 certGen.setIssuerDN( dnName );
259 certGen.setNotBefore( startDate );
260 certGen.setNotAfter( expiryDate );
261 certGen.setSubjectDN( dnName );
262 certGen.setPublicKey( publicKey );
263 certGen.setSignatureAlgorithm( "SHA1With" + ALGORITHM );
264
265 try
266 {
267 X509Certificate cert = certGen.generate( privateKey, "BC" );
268 entry.put( USER_CERTIFICATE_AT, cert.getEncoded() );
269 LOG.debug( "X509 Certificate: {}", cert );
270 }
271 catch ( Exception e )
272 {
273 LdapException ne = new LdapException( I18n.err( I18n.ERR_292 ) );
274 ne.initCause( e );
275 throw ne;
276 }
277
278 LOG.info( "Keys and self signed certificate successfully generated." );
279 }
280
281
282 /**
283 * @see #addKeyPair(ServerEntry)
284 *
285 * TODO the code is duplicate atm, will eliminate this redundancy after finding
286 * a better thought (an instant one is to call this method from the aboveaddKeyPair(entry) and remove the impl there)
287 */
288 public static void addKeyPair( ServerEntry entry, String issuerDN, String subjectDN, String keyAlgo ) throws LdapException
289 {
290 EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
291
292 if ( objectClass == null )
293 {
294 entry.put( SchemaConstants.OBJECT_CLASS_AT, TLS_KEY_INFO_OC, SchemaConstants.INET_ORG_PERSON_OC );
295 }
296 else
297 {
298 objectClass.add( TLS_KEY_INFO_OC, SchemaConstants.INET_ORG_PERSON_OC );
299 }
300
301 KeyPairGenerator generator = null;
302 try
303 {
304 generator = KeyPairGenerator.getInstance( keyAlgo );
305 }
306 catch ( NoSuchAlgorithmException e )
307 {
308 LdapException ne = new LdapException( I18n.err( I18n.ERR_291 ) );
309 ne.initCause( e );
310 throw ne;
311 }
312
313 generator.initialize( KEY_SIZE );
314 KeyPair keypair = generator.genKeyPair();
315 entry.put( KEY_ALGORITHM_AT, keyAlgo );
316
317 // Generate the private key attributes
318 PrivateKey privateKey = keypair.getPrivate();
319 entry.put( PRIVATE_KEY_AT, privateKey.getEncoded() );
320 entry.put( PRIVATE_KEY_FORMAT_AT, privateKey.getFormat() );
321 LOG.debug( "PrivateKey: {}", privateKey );
322
323 PublicKey publicKey = keypair.getPublic();
324 entry.put( PUBLIC_KEY_AT, publicKey.getEncoded() );
325 entry.put( PUBLIC_KEY_FORMAT_AT, publicKey.getFormat() );
326 LOG.debug( "PublicKey: {}", publicKey );
327
328 // Generate the self-signed certificate
329 Date startDate = new Date();
330 Date expiryDate = new Date( System.currentTimeMillis() + YEAR_MILLIS );
331 BigInteger serialNumber = BigInteger.valueOf( System.currentTimeMillis() );
332
333 X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
334 X500Principal issuerName = new X500Principal( issuerDN );
335 X500Principal subjectName = new X500Principal( subjectDN );
336
337 certGen.setSerialNumber( serialNumber );
338 certGen.setIssuerDN( issuerName );
339 certGen.setNotBefore( startDate );
340 certGen.setNotAfter( expiryDate );
341 certGen.setSubjectDN( subjectName );
342 certGen.setPublicKey( publicKey );
343 certGen.setSignatureAlgorithm( "SHA1With" + keyAlgo );
344
345 try
346 {
347 X509Certificate cert = certGen.generate( privateKey, "BC" );
348 entry.put( USER_CERTIFICATE_AT, cert.getEncoded() );
349 LOG.debug( "X509 Certificate: {}", cert );
350 }
351 catch ( Exception e )
352 {
353 LdapException ne = new LdapException( I18n.err( I18n.ERR_292 ) );
354 ne.initCause( e );
355 throw ne;
356 }
357
358 LOG.info( "Keys and self signed certificate successfully generated." );
359 }
360
361 }