一般我们在form提交时,可能会担心我们的参数显示给用户看。

我们主要解决的是以get方式进行url参数传递的问题,如:

http://xxx.xxx.xxx.xxx/xxx/xxx.do?pId=101&imageName=processimage.jpg这样的urlpId=101&imageName=processimage.jgp这样的参数如何进行加密。

 

我们采用:

Java.NET.URLEncoder.encode(Base64编码(加密字串),StringCode) 这样的方法来对url中的参数进行加密。

首先我们先说一下如何加密。

一、算法的选择:

对于url中参数进行加密的过程,我不建议使用RSA或者是三重DES这样的加密算法,主要原因在于性能和速度会受影响。

我建议大家使用对称加密如:DES或者是PBE算法。

我们在这边就使用PBEWithMD5AndDES来实现加密。

二、加密原理

对于一个纯文本,加密后它会变成一堆乱码,这堆乱码包括了许多非法字符,我们不希望把这些字符放入bean中,因此在加密完后,我们还要对加密结果进行base64编码。

PBE从字面上理解,它必须使用一个口令,我们不希望我们的加密过于复杂而影响页面跳转的速度,因此我们不采用口令+KEY的形式,我们这边的口令就是我们的KEY。

因此,我们的整个加密过程实现如下:

输入口令(KEY)--> 加密文本 --> 以base64对加密后的结果进行编码-->以java.net.URLEncoder.encode编码成浏览器可以识别的形式-->传输给接受的action。

而解密过程如下:

接受的action得到参数-->以base64对结果进行解码-->得到纯加密文本-->解密-->得到解密后的值。

三、BASE64

这边对于BASE64的原理不多说了,只说实现,目前网上有很多实现方式,有自己写的,有用sun.misc.*的,我们在这个例子里将使用javax.mail.internet.MimeUtility自带的base64编码工具。

需要引入activation.jar和mail.jar两个包。下面是具体的实现:

1. package com.xmobo.merchantAPI.utils;  
2.   
3. import java.io.ByteArrayInputStream;  
4. import java.io.ByteArrayOutputStream;  
5. import java.io.InputStream;  
6. import java.io.OutputStream;  
7. import javax.mail.internet.MimeUtility;  
8.   
9. public class Base64 {  
10.   
11. public static byte[] encode(byte[] b) throws Exception {  
12. null;  
13. null;  
14. try {  
15. new ByteArrayOutputStream();  
16. "base64");  
17.             b64os.write(b);  
18.             b64os.close();  
19. return baos.toByteArray();  
20. catch (Exception e) {  
21. throw new Exception(e);  
22. finally {  
23. try {  
24. if (baos != null) {  
25.                     baos.close();  
26. null;  
27.                 }  
28. catch (Exception e) {  
29.             }  
30. try {  
31. if (b64os != null) {  
32.                     b64os.close();  
33. null;  
34.                 }  
35. catch (Exception e) {  
36.             }  
37.         }  
38.     }  
39.   
40. public static byte[] decode(byte[] b) throws Exception {  
41. null;  
42. null;  
43. try {  
44. new ByteArrayInputStream(b);  
45. "base64");  
46. byte[] tmp = new byte[b.length];  
47. int n = b64is.read(tmp);  
48. byte[] res = new byte[n];  
49. 0, res, 0, n);  
50. return res;  
51. catch (Exception e) {  
52. throw new Exception(e);  
53. finally {  
54. try {  
55. if (bais != null) {  
56.                     bais.close();  
57. null;  
58.                 }  
59. catch (Exception e) {  
60.             }  
61. try {  
62. if (b64is != null) {  
63.                     b64is.close();  
64. null;  
65.                 }  
66. catch (Exception e) {  
67.             }  
68.         }  
69.     }  
70. }

四、加密解密工具类的实现

有了BASE64的工具类,下面的工作将变得简单了,编写我们的加密解密工具类吧:

1. package com.xmobo.merchantAPI.utils;  
2.   
3. import java.security.*;  
4. import javax.crypto.*;  
5. import javax.crypto.spec.*;  
6. import org.apache.commons.logging.Log;  
7. import org.apache.commons.logging.LogFactory;  
8.   
9. public class SecurityHelper {  
10. protected final static Log logger = LogFactory.getLog(SecurityHelper.class);  
11. private final static int ITERATIONS = 20;  
12.   
13. public static String encrypt(String key, String plainText) throws Exception {  
14. // String encryptTxt = "";  
15. try {  
16. byte[] salt = new byte[8];  
17. "MD5");  
18.             md.update(key.getBytes());  
19. byte[] digest = md.digest();  
20. for (int i = 0; i < 8; i++) {  
21.                 salt[i] = digest[i];  
22.             }  
23. new PBEKeySpec(key.toCharArray());  
24. "PBEWithMD5AndDES");  
25.             SecretKey skey = keyFactory.generateSecret(pbeKeySpec);  
26. new PBEParameterSpec(salt, ITERATIONS);  
27. "PBEWithMD5AndDES");  
28.             cipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);  
29. byte[] cipherText = cipher.doFinal(plainText.getBytes());  
30. new String(Base64.encode(salt));  
31. new String(Base64.encode(cipherText));  
32. return saltString + ciphertextString;  
33. catch (Exception e) {  
34. throw new Exception("Encrypt Text Error:" + e.getMessage(), e);  
35.         }  
36.     }  
37.   
38. public static String decrypt(String key, String encryptTxt) throws Exception {  
39. int saltLength = 12;  
40. try {  
41. 0, saltLength);  
42.             String ciphertext = encryptTxt.substring(saltLength, encryptTxt.length());  
43. byte[] saltarray = Base64.decode(salt.getBytes());  
44. byte[] ciphertextArray = Base64.decode(ciphertext.getBytes());  
45. new PBEKeySpec(key.toCharArray());  
46. "PBEWithMD5AndDES");  
47.             SecretKey skey = keyFactory.generateSecret(keySpec);  
48. new PBEParameterSpec(saltarray, ITERATIONS);  
49. "PBEWithMD5AndDES");  
50.             cipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);  
51. byte[] plaintextArray = cipher.doFinal(ciphertextArray);  
52. return new String(plaintextArray);  
53. catch (Exception e) {  
54. throw new Exception(e);  
55.         }  
56.     }  
57. }

注意上面加粗的三处地方:

private final static int ITERATIONS = 20;

上面的值越大,加密越深,一般例子都以"Java安全性编程指南”这本书中的例子的值为准,设成1000,我们在这边只需要20就够了,原因就是考虑到加解密的速度问题。

int saltLength = 12;

这是base64解码后源的长度,加密后再经BASE64编码后源的长度为8,BASE64解码后源的长度为12,至于为什么,这也是根据BASE64的原理得出的,具体可以看BASE64原理,网上很多,说得也都很简单。

PBEWithMD5AndDES

我们使用的是PBEWithMD5AndDES加密。

 

下面编写一个测试类

1. public static void main(String[] args) {  
2. "";  
3. "hello oh my god";  
4. try {  
5.             System.out.println(plainTxt);  
6. "mypassword01", plainTxt);  
7. "mypassword01", encryptTxt);  
8.             System.out.println(encryptTxt);  
9.             System.out.println(plainTxt);  
10. catch (Exception e) {  
11.             e.printStackTrace();  
12. 1);  
13.         }  
14.     }

五、工具类在struts action中的具体使用

1. MyTaskDTO taskDTO = new MyTaskDTO();  
2.     TaskInstance ti = (TaskInstance) it.next();  
3.     taskDTO.setTaskName(ti.getName());  
4.     taskDTO.setTaskCreateDate(sd.format(ti.getCreate()));  
5.     taskDTO.setTaskDescr(ti.getDescription());  
6. /* no encrypted data */  
7.     String taskId = String.valueOf(ti.getId());  
8.     String tokenId = String.valueOf(ti.getToken().getId());  
9.     processImgName = PropertyUtil.getProperty(  
10.       Constants.BPM_PROCESS_PAYMENT_PROCESSIMAGE).toString()  
11. ".jpg";  
12.     processDefId = String.valueOf(ti.getToken()  
13.       .getProcessInstance().getProcessDefinition().getId());  
14.   
15. /* encrypted data */<strong>  
16.     </strong>taskId = EncryptUrlPara.encrypt(taskId);  
17.     tokenId = EncryptUrlPara.encrypt(tokenId);  
18.     processImgName = EncryptUrlPara.encrypt(processImgName);  
19.     processDefId = EncryptUrlPara.encrypt(processDefId);  
20.   
21.     taskDTO.setTaskId(taskId);  
22.     taskDTO.setTokenId(tokenId);  
23.     taskDTO.setProcessDefinitionId(processDefId);  
24.     taskDTO.setProcessImageName(processImgName);

六、jsp页面中的encode

把上述这个bean放入request中,带到下一个jsp页面中后,在jsp页面的处理如下:

 

1.    String processImgPath=taskDTO.getProcessImageName();  
2.   
3.    String processDefId=taskDTO.getProcessDefinitionId();  
4. <strong>  
5. "UTF-8");  
6.   
7. "UTF-8");  
8.   
9. "/queryMyTask.do";

1. <a href="<%=showProcessImgUrl%>?method=showProcessImg&processDefinitionId=<%=processDefId%>&processImgPath=<%=processImgPath%>"target="_blank"><u><span class="left_txt">查看当前进程</span></u></a>

七、在接受加密参数的action中对加密的值进行解密

我们假设我们的接受的action为: queryMyTask.do,它接受一系列的参数,基中,processDefId和processImgPath是加密的。

 

实现如下:

1. String processImgFilePath = "";  
2.   
3. String processDefinitionId = (String)request.getParameter("processDefinitionId");  
4.   
5. processImgFilePath = (String)request.getParameter("processImgPath");  
6.    
7. processDefinitionId =EncryptUrlPara.decrypt(processDefinitionId);  
8.   
9. processImgFilePath =EncryptUrlPara.decrypt(processImgFilePath);

 

需要注意的是此处不需要再decode了。

八、key(口令)的存放

因为我们这边的key就是口令,是一个文本,我们将它存放在server端的properties中,当然,我们也是加密存放的。

我们使用spring+jasypt1.5(Java simple encrypt包)。

设我们有一个properties文件,其中:

security.des.key=ENC(OlO0LqELUuLOVreCtDngHaNgMcZWUyUg)

这个就是我们在encrypt和decrypt方法中用到的key。

我们不希望这个key以明文的形式设在properties中,我们对这个key再进行一次加密用的同样也是PBEWithMD5AndDES,当然因为有了spring因为有了jasypt包,因此这个过程一切是自动的。

我们使用jasypt包下的bin中自带的encrypt.bat工具:

encrypt input=mykey password=secret algorithm=PBEWithMD5AndDES

该命令会输出一行乱码,把这行乱码复制到properties文件中,在外层加上ENC(),如:

生成: OlO0LqELUuLOVreCtDngHaNgMcZWUyUg

放入properties后需要转换成: ENC(OlO0LqELUuLOVreCtDngHaNgMcZWUyUg)

然后在工程部署的机器上需要设一个环境变理,如:

set APP_ENCRYPTION_PASSWORD=secret   此处的值必须和上面encrypt.bat命令行中的password=后的值一样。

(Linux请用export APP_ENCRYPTION_PASSWORD=secret)

然后配置spring,使该properties在工程被app 容器load时,自动解密,这样我们在我们的方法中直接取到该KEY时就已经是明文了(解密过程是jasypt+spring自动完成的),以下是这一步配置的详细内容:

 

1. <context:component-scanbase-package="jbpmweb" />  
2.   
3. "environmentVariablesConfiguration"  
4.   
5. class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig"  
6.   
7. "<strong>PBEWithMD5AndDES</strong>"p:passwordEnvName="APP_ENCRYPTION_PASSWORD" />  
8.   
9.    
10.   
11.  <!--  
12.   
13. for decrypting configuration values.  
14.   
15.  -->  
16.   
17.    
18.   
19. "configurationEncryptor"class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor"  
20.   
21. "environmentVariablesConfiguration" />  
22.   
23.    
24.   
25. "propertyConfigurer"  
26.   
27. class="org.jasypt.spring.properties.EncryptablePropertyPlaceholderConfigurer">  
28.   
29. "configurationEncryptor" />  
30.   
31. "locations">  
32.   
33.   <list>  
34.   
35.    <value>classpath:xxx.properties</value>  
36.   
37.   </list>  
38.   
39.  </property>  
40.   
41.  </bean>  
42.   
43.    
44.   
45.  <!--  
46.   
47.  Configurer that replaces ${...} placeholders with values from a  
48.   
49.  properties file  
50.   
51.  -->  
52.   
53. "classpath:jbpmweb.properties" />  
54.   
55.    
56.   
57. "commonsConfigurationFactoryBean"class="xxx.xxx.CommonsConfigurationFactoryBean"  
58.   
59. "SYSTEM_PROPERTIES_MODE_OVERRIDE"p:encryptor-ref="configurationEncryptor">  
60.   
61.  <constructor-arg>  
62.   
63. class="org.apache.commons.configuration.PropertiesConfiguration">  
64.   
65. "xxx.properties" />  
66.   
67.   </bean>  
68.   
69.  </constructor-arg>  
70.   
71.  </bean>  
72.    
73.   
74. "propertiesConfiguration"factory-bean="&commonsConfigurationFactoryBean"  
75.   
76. "getConfiguration"/>

九、注意

Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream

如果你用myEclipse进行开发的话,运行时可能会出现以下的错误: Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream 原因是jar包版本不统一,解决方法如下: 删除java EE 5 Libraries/javaee.jar/mail里的包有东西. 具体方法如下: 用rar打开D:\MyEclipse8.6\Genuitec\Common\plugins\com.genuitec.eclipse.j2eedt.core_8.6.0.me201007292038\data\libraryset\EE_5\javaee.jar ,然后删除mail,一切就ok了。