View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.ksb.security.admin.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.bouncycastle.jce.X509Principal;
20  import org.bouncycastle.x509.X509V3CertificateGenerator;
21  import org.kuali.rice.core.api.config.property.Config;
22  import org.kuali.rice.core.api.config.property.ConfigContext;
23  import org.kuali.rice.ksb.security.admin.KeyStoreEntryDataContainer;
24  import org.kuali.rice.ksb.security.admin.service.JavaSecurityManagementService;
25  import org.springframework.beans.factory.InitializingBean;
26  
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.IOException;
30  import java.security.GeneralSecurityException;
31  import java.security.KeyPair;
32  import java.security.KeyPairGenerator;
33  import java.security.KeyStore;
34  import java.security.KeyStoreException;
35  import java.security.NoSuchAlgorithmException;
36  import java.security.PrivateKey;
37  import java.security.Security;
38  import java.security.UnrecoverableEntryException;
39  import java.security.cert.Certificate;
40  import java.util.ArrayList;
41  import java.util.Calendar;
42  import java.util.Date;
43  import java.util.Enumeration;
44  import java.util.List;
45  
46  /**
47   * This is an implementation of the {@link JavaSecurityManagementService} interface used by the KSB module 
48   * 
49   * @author Kuali Rice Team (rice.collab@kuali.org)
50   *
51   */
52  public class JavaSecurityManagementServiceImpl implements JavaSecurityManagementService, InitializingBean {
53  
54      protected final String CLIENT_KEY_GENERATOR_ALGORITHM = "RSA";
55      protected final String CLIENT_SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
56      protected final int CLIENT_KEY_PAIR_KEY_SIZE = 512;
57      private final int CLIENT_CERT_EXPIRATION_DAYS = 9999;
58  
59      private static final String MODULE_SHA_RSA_ALGORITHM = "SHA1withRSA";
60      private static final String MODULE_JKS_TYPE = "JKS";
61  
62      private String moduleKeyStoreLocation;
63      private String moduleKeyStoreAlias;
64      private String moduleKeyStorePassword;
65  
66      private KeyStore moduleKeyStore;
67      private PrivateKey modulePrivateKey;
68  
69      /**
70       * Load the module's keystore and private key for this "application"
71       */
72      public void afterPropertiesSet() throws Exception {
73          if (StringUtils.isEmpty(getModuleKeyStoreLocation())) {
74              setModuleKeyStoreLocation(ConfigContext.getCurrentContextConfig().getKeystoreFile());
75          }
76          if (StringUtils.isEmpty(getModuleKeyStoreAlias())) {
77              setModuleKeyStoreAlias(ConfigContext.getCurrentContextConfig().getKeystoreAlias());
78          }
79          if (StringUtils.isEmpty(getModuleKeyStorePassword())) {
80              setModuleKeyStorePassword(ConfigContext.getCurrentContextConfig().getKeystorePassword());
81          }
82          verifyConfiguration();
83          this.moduleKeyStore = loadKeyStore();
84          this.modulePrivateKey = loadPrivateKey();
85      }
86  
87      /**
88       * Verifies the configuration of this service and throws an exception if it is not configured properly.
89       */
90      protected void verifyConfiguration() {
91          if (StringUtils.isEmpty(getModuleKeyStoreLocation())) {
92              throw new RuntimeException("Value for configuration parameter '" + Config.KEYSTORE_FILE + "' could not be found.  Please ensure that the keystore is configured properly.");
93          }
94          if (StringUtils.isEmpty(getModuleKeyStoreAlias())) {
95              throw new RuntimeException("Value for configuration parameter '" + Config.KEYSTORE_ALIAS + "' could not be found.  Please ensure that the keystore is configured properly.");
96          }
97          if (StringUtils.isEmpty(getModuleKeyStorePassword())) {
98              throw new RuntimeException("Value for configuration parameter '" + Config.KEYSTORE_PASSWORD + "' could not be found.  Please ensure that the keystore is configured properly.");
99          }
100         File keystoreFile = new File(getModuleKeyStoreLocation());
101         if (!keystoreFile.exists()) {
102             throw new RuntimeException("Value for configuration parameter '" + Config.KEYSTORE_FILE + "' is invalid.  The file does not exist on the filesystem, location was: '" + getModuleKeyStoreLocation() + "'");
103         }
104         if (!keystoreFile.canRead()) {
105             throw new RuntimeException("Value for configuration parameter '" + Config.KEYSTORE_FILE + "' is invalid.  The file exists but is not readable (please check permissions), location was: '" + getModuleKeyStoreLocation() + "'");
106         }
107     }
108 
109     protected KeyStore loadKeyStore() throws GeneralSecurityException, IOException {
110         KeyStore keyStore = KeyStore.getInstance(getModuleKeyStoreType());
111         FileInputStream stream = null;
112         try {
113             stream = new FileInputStream(getModuleKeyStoreLocation());
114             keyStore.load(stream, getModuleKeyStorePassword().toCharArray());
115             stream.close();
116         } catch (Exception e) {
117             if (stream != null) {
118                 try {
119                     stream.close();
120                 } catch (Exception ignored) {
121                 }
122             }
123         }
124         return keyStore;
125     }
126 
127     protected PrivateKey loadPrivateKey() throws GeneralSecurityException {
128         return (PrivateKey)getModuleKeyStore().getKey(getModuleKeyStoreAlias(), getModuleKeyStorePassword().toCharArray());
129     }
130 
131     public void removeClientCertificate(String alias) throws KeyStoreException {
132         KeyStore moduleKeyStore = getModuleKeyStore();
133         if (!moduleKeyStore.entryInstanceOf(alias, KeyStore.TrustedCertificateEntry.class)) {
134             throw new RuntimeException("Only entries of type " + KeyStoreEntryDataContainer.DISPLAYABLE_ENTRY_TYPES.get(KeyStore.TrustedCertificateEntry.class) + " can be removed");
135         }
136         getModuleKeyStore().deleteEntry(alias);
137     }
138     
139     protected void addClientCertificateToModuleKeyStore(String alias, Certificate clientCertificate) throws KeyStoreException {
140         getModuleKeyStore().setEntry(alias, new KeyStore.TrustedCertificateEntry(clientCertificate), null);
141     }
142     
143     public boolean isAliasInKeystore(String alias) throws KeyStoreException {
144         return getModuleKeyStore().containsAlias(alias);
145     }
146     
147     public String getCertificateAlias(Certificate certificate) throws KeyStoreException {
148         return getModuleKeyStore().getCertificateAlias(certificate);
149     }
150     
151     public KeyStore generateClientKeystore(String alias, String clientPassphrase) throws GeneralSecurityException {
152         if (isAliasInKeystore(alias)) {
153             throw new KeyStoreException("Alias '" + alias + "' already exists in module keystore");
154         }
155 //        Certificate[] clientCertificateChain = {};
156 //        PrivateKey clientPrivateKey = null;
157         KeyStore ks = null;
158         try {
159             // generate a key pair for the client
160             KeyPairGenerator keyGen = KeyPairGenerator.getInstance(CLIENT_KEY_GENERATOR_ALGORITHM);
161 //            SecureRandom random = SecureRandom.getInstance(CLIENT_SECURE_RANDOM_ALGORITHM);
162             keyGen.initialize(CLIENT_KEY_PAIR_KEY_SIZE);
163 //            keyGen.initialize(new RSAKeyGenParameterSpec(512,RSAKeyGenParameterSpec.F0));
164             KeyPair pair = keyGen.generateKeyPair();
165 
166 //            PublicKey clientPublicKey = pair.getPublic();
167 //            clientPrivateKey = pair.getPrivate();
168 //            // generate the Certificate
169 //            X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
170 ////            X509Name nameInfo = new X509Name(false,"CN=" + alias);
171 //            certificateGenerator.setSignatureAlgorithm("MD5WithRSA");
172 //            certificateGenerator.setSerialNumber(new java.math.BigInteger("1"));
173 //            X509Principal nameInfo = new X509Principal("CN=" + alias);
174 //            certificateGenerator.setIssuerDN(nameInfo);
175 //            certificateGenerator.setSubjectDN(nameInfo);                       // note: same as issuer
176 //            certificateGenerator.setNotBefore(new Date());
177 //            Calendar c = Calendar.getInstance();
178 //            c.add(Calendar.DATE, CLIENT_CERT_EXPIRATION_DAYS);
179 //            certificateGenerator.setNotAfter(c.getTime());
180 //            certificateGenerator.setPublicKey(clientPublicKey);
181 //            X509Certificate cert = certificateGenerator.generateX509Certificate(clientPrivateKey);
182 //            clientCertificateChain = new Certificate[]{cert};
183 //
184 //            // generate client keyStore file
185 //            ks = KeyStore.getInstance(getModuleKeyStoreType());
186 //            ks.load(null, clientPassphrase.toCharArray());
187 //            // set client private key on keyStore file
188 //            ks.setEntry(alias, new KeyStore.PrivateKeyEntry(clientPrivateKey, clientCertificateChain), new KeyStore.PasswordProtection(clientPassphrase.toCharArray()));
189             Certificate cert = generateCertificate(pair, alias);
190             ks = generateKeyStore(cert, pair.getPrivate(), alias, clientPassphrase);
191             
192             // set the module certificate on the client keyStore file
193             ks.setEntry(getModuleKeyStoreAlias(), new KeyStore.TrustedCertificateEntry(getCertificate(getModuleKeyStoreAlias())),  null);
194 
195             // add the client certificate to the module keyStore
196             addClientCertificateToModuleKeyStore(alias, cert);
197             
198             return ks;
199         } catch (IOException e) {
200             throw new RuntimeException("Could not create new KeyStore",e);
201         }
202     }
203     
204     protected Certificate generateCertificate(KeyPair keyPair, String alias) throws GeneralSecurityException {
205       
206       //test that Bouncy Castle provider is present and add it if it's not
207       if( Security.getProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME) == null) {
208     	  Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
209       }
210       X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
211 //      X509Name nameInfo = new X509Name(false,"CN=" + alias);
212       certificateGenerator.setSignatureAlgorithm("MD5WithRSA");
213       certificateGenerator.setSerialNumber(new java.math.BigInteger("1"));
214       X509Principal nameInfo = new X509Principal("CN=" + alias);
215       certificateGenerator.setIssuerDN(nameInfo);
216       certificateGenerator.setSubjectDN(nameInfo);  // note: same as issuer for self signed
217       certificateGenerator.setNotBefore(new Date());
218       Calendar c = Calendar.getInstance();
219       c.add(Calendar.DATE, CLIENT_CERT_EXPIRATION_DAYS);
220       certificateGenerator.setNotAfter(c.getTime());
221       certificateGenerator.setPublicKey(keyPair.getPublic());
222       return certificateGenerator.generate(keyPair.getPrivate(), org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME);
223     }
224     
225     protected KeyStore generateKeyStore(Certificate cert, PrivateKey privateKey, String alias, String keyStorePassword) throws GeneralSecurityException, IOException {
226         KeyStore ks = KeyStore.getInstance(getModuleKeyStoreType());
227         ks.load(null, keyStorePassword.toCharArray());
228         // set client private key on keyStore file
229         ks.setEntry(alias, new KeyStore.PrivateKeyEntry(privateKey, new Certificate[]{cert}), new KeyStore.PasswordProtection(keyStorePassword.toCharArray()));
230         return ks;
231     }
232 
233     public List<KeyStoreEntryDataContainer> getListOfModuleKeyStoreEntries() {
234         List<KeyStoreEntryDataContainer> keyStoreEntries = new ArrayList<KeyStoreEntryDataContainer>();
235         try {
236             KeyStore moduleKeyStore = getModuleKeyStore();
237 
238             // List the aliases
239             for (Enumeration<String> enumer = moduleKeyStore.aliases(); enumer.hasMoreElements();) {
240                 String alias = (String) enumer.nextElement();
241                 KeyStoreEntryDataContainer dataContainer = new KeyStoreEntryDataContainer(alias,moduleKeyStore.getCreationDate(alias));
242                 KeyStore.PasswordProtection passwordProtection = null;
243                 if (moduleKeyStore.isKeyEntry(alias)) {
244                     passwordProtection = new KeyStore.PasswordProtection(getModuleKeyStorePassword().toCharArray());
245                 }
246                 KeyStore.Entry entry = moduleKeyStore.getEntry(alias, passwordProtection);
247                 dataContainer.setType(entry.getClass());
248                 keyStoreEntries.add(dataContainer);
249             }
250         } catch (KeyStoreException e) {
251             e.printStackTrace();
252             throw new RuntimeException(e);
253         } catch (NoSuchAlgorithmException e) {
254             e.printStackTrace();
255             throw new RuntimeException(e);
256         } catch (UnrecoverableEntryException e) {
257             e.printStackTrace();
258             throw new RuntimeException(e);
259         }
260         return keyStoreEntries;
261     }
262 
263     public String getModuleSignatureAlgorithm() {
264         return getModuleAlgorithm();
265     }
266 
267     /**
268      * @see java.security.KeyStore#getCertificate(java.lang.String)
269      */
270     public Certificate getCertificate(String alias) throws KeyStoreException {
271         return getModuleKeyStore().getCertificate(alias);
272     }
273     
274     protected String getModuleKeyStoreType() {
275         return MODULE_JKS_TYPE;
276     }
277 
278     protected String getModuleAlgorithm() {
279         return MODULE_SHA_RSA_ALGORITHM;
280     }
281 
282     public String getModuleKeyStoreLocation() {
283         return this.moduleKeyStoreLocation;
284     }
285 
286     public void setModuleKeyStoreLocation(String moduleKeyStoreLocation) {
287         this.moduleKeyStoreLocation = moduleKeyStoreLocation;
288     }
289 
290     public String getModuleKeyStoreAlias() {
291         return this.moduleKeyStoreAlias;
292     }
293 
294     public void setModuleKeyStoreAlias(String moduleKeyStoreAlias) {
295         this.moduleKeyStoreAlias = moduleKeyStoreAlias;
296     }
297 
298     public String getModuleKeyStorePassword() {
299         return this.moduleKeyStorePassword;
300     }
301 
302     public void setModuleKeyStorePassword(String moduleKeyStorePassword) {
303         this.moduleKeyStorePassword = moduleKeyStorePassword;
304     }
305 
306     public KeyStore getModuleKeyStore() {
307         return this.moduleKeyStore;
308     }
309     
310     public PrivateKey getModulePrivateKey() {
311         return this.modulePrivateKey;
312     }
313     
314 }