View Javadoc

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