View Javadoc

1   /*
2    * Copyright 2006-2008 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.core.service.impl;
17  
18  import java.io.UnsupportedEncodingException;
19  import java.security.GeneralSecurityException;
20  import java.security.MessageDigest;
21  
22  import javax.crypto.Cipher;
23  import javax.crypto.KeyGenerator;
24  import javax.crypto.SecretKey;
25  import javax.crypto.SecretKeyFactory;
26  import javax.crypto.spec.DESKeySpec;
27  
28  import org.apache.commons.codec.binary.Base64;
29  import org.apache.commons.lang.StringUtils;
30  import org.kuali.rice.core.config.ConfigContext;
31  import org.kuali.rice.core.service.Demonstration;
32  import org.kuali.rice.core.service.EncryptionService;
33  
34  /**
35   * Implementation of encryption service for demonstration. 
36   * 
37   * @author Kuali Rice Team (rice.collab@kuali.org)
38   */
39  public class DemonstrationGradeEncryptionServiceImpl implements EncryptionService, Demonstration {
40      public final static String ALGORITHM = "DES/ECB/PKCS5Padding";
41      public final static String HASH_ALGORITHM = "SHA"; 
42  
43      private final static String CHARSET = "UTF-8";
44  
45      private transient SecretKey desKey;
46  
47      private boolean isEnabled = false;
48  
49      public DemonstrationGradeEncryptionServiceImpl() throws Exception {
50          if (desKey != null) {
51              throw new RuntimeException("The secret key must be kept secret. Storing it in the Java source code is a really bad idea.");
52          }
53          String key = ConfigContext.getCurrentContextConfig().getProperty("encryption.key");
54          if (!StringUtils.isEmpty(key)) {
55              setSecretKey(key);
56          }
57      }
58      
59      public boolean isEnabled() {
60          return isEnabled;
61      }
62  
63      public String encrypt(Object valueToHide) throws GeneralSecurityException {
64      	checkEnabled();
65      	
66          if (valueToHide == null) {
67              return "";
68          }
69  
70          // Initialize the cipher for encryption
71          Cipher cipher = Cipher.getInstance(ALGORITHM);
72          cipher.init(Cipher.ENCRYPT_MODE, desKey);
73  
74          try {
75              // Our cleartext
76              byte[] cleartext = valueToHide.toString().getBytes(CHARSET);
77      
78              // Encrypt the cleartext
79              byte[] ciphertext = cipher.doFinal(cleartext);
80      
81              return new String(Base64.encodeBase64(ciphertext), CHARSET);
82          } catch (Exception e) {
83              throw new RuntimeException(e);
84          }
85  
86      }
87  
88      public String decrypt(String ciphertext) throws GeneralSecurityException {
89      	checkEnabled();
90      	
91          if (StringUtils.isBlank(ciphertext)) {
92              return "";
93          }
94  
95          // Initialize the same cipher for decryption
96          Cipher cipher = Cipher.getInstance(ALGORITHM);
97          cipher.init(Cipher.DECRYPT_MODE, desKey);
98  
99          try {
100             // un-Base64 encode the encrypted data
101             byte[] encryptedData = Base64.decodeBase64(ciphertext.getBytes(CHARSET));
102 
103             // Decrypt the ciphertext
104             byte[] cleartext1 = cipher.doFinal(encryptedData);
105             return new String(cleartext1, CHARSET);
106         } catch (UnsupportedEncodingException e) {
107             throw new RuntimeException(e);
108         }
109     }
110 
111     public byte[] encryptBytes(byte[] valueToHide) throws GeneralSecurityException {
112     	checkEnabled();
113     	
114         if (valueToHide == null) {
115             return new byte[0];
116         }
117 
118         // Initialize the cipher for encryption
119         Cipher cipher = Cipher.getInstance(ALGORITHM);
120         cipher.init(Cipher.ENCRYPT_MODE, desKey);
121 
122         // Our cleartext
123         byte[] cleartext = valueToHide;
124 
125         // Encrypt the cleartext
126         byte[] ciphertext = cipher.doFinal(cleartext);
127 
128         return ciphertext;
129     }
130 
131     public byte[] decryptBytes(byte[] ciphertext) throws GeneralSecurityException {
132     	checkEnabled();
133     	
134         if (ciphertext == null) {
135             return new byte[0];
136         }
137 
138         // Initialize the same cipher for decryption
139         Cipher cipher = Cipher.getInstance(ALGORITHM);
140         cipher.init(Cipher.DECRYPT_MODE, desKey);
141 
142         // un-Base64 encode the encrypted data
143         byte[] encryptedData = ciphertext;
144 
145         // Decrypt the ciphertext
146         byte[] cleartext1 = cipher.doFinal(encryptedData);
147         return cleartext1;
148     }
149     
150     /**
151      * 
152      * This method generates keys. This method is implementation specific and should not be present in any general purpose interface
153      * extracted from this class.
154      * 
155      * @return
156      * @throws Exception
157      */
158     public static String generateEncodedKey() throws Exception {
159         KeyGenerator keygen = KeyGenerator.getInstance("DES");
160         SecretKey desKey = keygen.generateKey();
161 
162         // Create the cipher
163         Cipher cipher = Cipher.getInstance(ALGORITHM);
164         cipher.init((Cipher.WRAP_MODE), desKey);
165         
166         SecretKeyFactory desFactory = SecretKeyFactory.getInstance("DES");
167         DESKeySpec desSpec = (DESKeySpec) desFactory.getKeySpec(desKey, javax.crypto.spec.DESKeySpec.class);
168         byte[] rawDesKey = desSpec.getKey();
169 
170         return new String(Base64.encodeBase64(rawDesKey));
171     }
172 
173     private SecretKey unwrapEncodedKey(String key) throws Exception {
174         KeyGenerator keygen = KeyGenerator.getInstance("DES");
175         SecretKey desKey = keygen.generateKey();
176 
177         // Create the cipher
178         Cipher cipher = Cipher.getInstance(ALGORITHM);
179         cipher.init((Cipher.UNWRAP_MODE), desKey);
180 
181         byte[] bytes = Base64.decodeBase64(key.getBytes());
182 
183         SecretKeyFactory desFactory = SecretKeyFactory.getInstance("DES");
184 
185         DESKeySpec keyspec = new DESKeySpec(bytes);
186         SecretKey k = desFactory.generateSecret(keyspec);
187 
188         return k;
189 
190     }
191 
192     /**
193      * Sets the secretKey attribute value.
194      * 
195      * @param secretKey The secretKey to set.
196      * @throws Exception
197      */
198     public void setSecretKey(String secretKey) throws Exception {
199     	if (!StringUtils.isEmpty(secretKey)) {
200     		desKey = this.unwrapEncodedKey(secretKey);
201     		isEnabled = true;
202     		// Create the cipher
203     		Cipher cipher = Cipher.getInstance(ALGORITHM);
204     		cipher.init((Cipher.WRAP_MODE), desKey);
205     	}
206     }
207 
208     /** Hash the value by converting to a string, running the hash algorithm, and then base64'ng the results.
209      * Returns a blank string if any problems occur or the input value is null or empty.
210      * 
211      * @see org.kuali.rice.kns.service.EncryptionService#hash(java.lang.Object)
212      */
213     public String hash(Object valueToHide) throws GeneralSecurityException {
214         if ( valueToHide == null || StringUtils.isEmpty( valueToHide.toString() ) ) {
215             return "";
216         }
217         try {
218             MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
219             return new String( Base64.encodeBase64( md.digest( valueToHide.toString().getBytes( CHARSET ) ) ), CHARSET );
220         } catch ( UnsupportedEncodingException ex ) {
221             // should never happen
222         }
223         return "";
224     }
225     
226     /**
227      * Performs a check to see if the encryption service is enabled.  If it is not then an
228      * IllegalStateException will be thrown.
229      */
230     protected void checkEnabled() {
231     	if (!isEnabled()) {
232     		throw new IllegalStateException("Illegal use of encryption service.  Ecryption service is disabled, to enable please configure 'encryption.key'.");
233     	}
234     }
235 
236     
237 
238 }