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 }