一、概述
Jasypt 這個Java類包為開發人員提供一種簡單的方式來為項目增加加密功能,包括:密碼Digest認證,文本和對象加密,集成 hibernate,Spring Security(Acegi)來增強密碼管理。
Jasypt是一個Java庫,可以使開發者不需太多操作來給Java項目添加基本加密功能,而且不需要知道加密原理。
根據Jasypt文檔,該技術可用於加密任務與應用程序,例如加密密碼、敏感信息和數據通信、創建完整檢查數據的sums. 其他性能包括高安全性、基於標准的加密技術、可同時單向和雙向加密的加密密碼、文本、數字和二進制文件。Jasypt也可以與Acegi Security整合也即Spring Security。Jasypt亦擁有加密應用配置的集成功能,而且提供一個開放的API從而任何一個Java Cryptography Extension都可以使用Jasypt。
Jasypt還符合RSA標准的基於密碼的加密,並提供了無配置加密工具以及新的、高可配置標准的加密工具。
1、該開源項目可用於加密任務與應用程序,例如加密密碼、敏感信息和數據通信
2、還包括高安全性、基於標准的加密技術、可同時單向和雙向加密的加密密碼、文本、數字和二進制文件。
3、Jasypt還符合RSA標准的基於密碼的加密,並提供了無配置加密工具以及新的、高可配置標准的加密工具。
4、加密屬性文件(encryptable properties files)、Spring work集成、加密Hibernate數據源配置、新的命令行工具、URL加密的Apache wicket集成以及升級文檔。
5、Jasypt也可以與Acegi Security整合也即Spring Security。Jasypt亦擁有加密應用配置的集成功能,而且提供一個開放的API從而任何一個Java Cryptography Extension都可以使用Jasypt。
二、使用
2.1、jar使用
2.1.1、shell下jar使用加密
Maven下載好的jar包加密\Maven\org\jasypt\jasypt\1.9.3\jasypt-1.9.3.jar
cd ~/.m2/repository/org/jasypt/jasypt/1.9.3
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI password=G0CvDz7oJn6 algorithm=PBEWithMD5AndDES input=root
輸出:
----ARGUMENTS------------------- input: root algorithm: PBEWithMD5AndDES password: G0CvDz7oJn6 ----OUTPUT---------------------- wo9sTA8V7t+kKHKtwzOVSw==
2.1.2、shell下jar使用解密
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringDecryptionCLI input=wo9sTA8V7t+kKHKtwzOVSw== password=G0CvDz7oJn6 algorithm=PBEWithMD5AndDES
解密值
----ARGUMENTS------------------- algorithm: PBEWithMD5AndDES input: wo9sTA8V7t+kKHKtwzOVSw== password: G0CvDz7oJn6 ----OUTPUT---------------------- root
2.1.3、詳細說明
加密入口類:org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI
解密入口類:org.jasypt.intf.cli.JasyptPBEStringDecryptionCLI
input:你要加、解密的字符串
password:密鑰口令
algorithm:加密算法
OUTPUT:下面的字符串就是生成的密文、明文
配置項
Key(配置項) | Required(必須) | Default Value(默認值) |
---|---|---|
jasypt.encryptor.password | True | - |
jasypt.encryptor.algorithm | False | PBEWithMD5AndDES |
jasypt.encryptor.keyObtentionIterations | False | 1000 |
jasypt.encryptor.poolSize | False | 1 |
jasypt.encryptor.providerName | False | SunJCE |
jasypt.encryptor.providerClassName | False | null |
jasypt.encryptor.saltGeneratorClassname | False | org.jasypt.salt.RandomSaltGenerator |
jasypt.encryptor.ivGeneratorClassname | False | org.jasypt.iv.NoIvGenerator,如果想使用java8+后的PBEWITHHMACSHA512ANDAES_256 則要設置成: org.jasypt.salt.RandomIVGenerator. |
jasypt.encryptor.stringOutputType | False | base64 |
jasypt.encryptor.proxyPropertySources | False | false |
2.2、代碼方式1-原生
2.2.1、加密解密-文本原生使用
<dependency> <groupId>org.jasypt</groupId> <artifactId>jasypt</artifactId> <version>1.9.3</version> </dependency>
示例
public class BasicTextEncryptorTest { BasicTextEncryptor textEncryptor; @Before public void setUp() { textEncryptor = new BasicTextEncryptor(); textEncryptor.setPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7"); } @Test public void encrypt() { // 加密 System.out.println(textEncryptor.encrypt("root@1234")); //TJetNWzmC4os1CCb+gHtz+5MpL9NFMML //KCTSu/Dv1elE1A/ZyppCHgJAAwKiez/p } @Test public void decyptPwd() { // 解密 // root@1234 System.out.println(textEncryptor.decrypt("TJetNWzmC4os1CCb+gHtz+5MpL9NFMML")); // root@1234 System.out.println(textEncryptor.decrypt("KCTSu/Dv1elE1A/ZyppCHgJAAwKiez/p")); } }
2.2.2、單向散列
一般在做用戶認證的時候,通常會使用MD5做簡單的散列,然后登錄時必須MD5值實現。同樣的需求,也可以使用Jasypt來實現。
BasicPasswordEncryptor
@Test public void encrypt() { BasicPasswordEncryptor textEncryptor = new BasicPasswordEncryptor(); String encryptPassword = textEncryptor.encryptPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7"); System.out.println(encryptPassword); boolean checkPassword = textEncryptor.checkPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7", encryptPassword); System.out.println(checkPassword); }
2.2.3、多線程加解密
在多核機器上運行時,我們希望並行處理解密處理。為了獲得良好的性能,我們可以使用PooledPBEStringEncryptor 和setPoolSize() API來創建一個解密線程池。它們中的每一個都可以由不同的線程並行使用:
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); encryptor.setPoolSize(4); encryptor.setPassword("some-random-data"); encryptor.setAlgorithm("PBEWithMD5AndTripleDES");
最好將池大小設置為等於機器的核心數。加密和解密的代碼與以前的代碼相同。
2.3、代碼方式2-springboot結合
源碼地址:https://github.com/ulisesbocchio/jasypt-spring-boot
pom
<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.1</version> </dependency>
增加JasyptConfig 配置類
@Configuration public class JasyptConfig { @Bean("jasyptStringEncryptor") public StringEncryptor stringEncryptor() { PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); SimpleStringPBEConfig config = new SimpleStringPBEConfig(); config.setPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7"); // config.setAlgorithm("PBEWithMD5AndDES");//默認配置 // config.setKeyObtentionIterations("1000");//默認配置 config.setPoolSize("4"); // config.setProviderName("SunJCE");//默認配置 // config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");//默認配置 // config.setStringOutputType("base64");//默認配置 encryptor.setConfig(config); return encryptor; } }
配置文件的寫入和Spring XML的基本類似。application.yml相當於applicationContext.xml,security.properties就是要進行屬性替換的配置文件。
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/abc?useSSL=false spring.datasource.username=root spring.datasource.password=${jdbc.password}
security.properties
jdbc.password=ENC(TJetNWzmC4os1CCb+gHtz+5MpL9NFMML)
啟動類增加
@SpringBootApplication @EnableEncryptableProperties @PropertySource(value = {"classpath:security.properties"},ignoreResourceNotFound = false) public class SpringRunnerMain { public static void main(String[] args) { SpringApplication.run(SpringRunnerMain.class, args); } }
2.4、關於這個口令的配置方式
jasypt的作者建議是把這個鹽值放在系統屬性、命令行或是環境變量來使用,而不是放在配置文件
還有種常用方式,直接配置方式(這樣口令就暴露在這個配置文件里面,不建議);密文使用ENC(……),密鑰放在代碼中,配置分開
1、啟動命令
jar: 命令:java -Djasypt.encryptor.password=jasypt -jar xxx.jar
war:到Tomcat的bin目錄下,打開文件catalina.bat/catalina.sh,添加如下參數,然后保存:window:set JAVA_OPTS="-Djasypt.encryptor.password=jasypt" , Linux:JAVA_OPTS="-Djasypt.encryptor.password=jasypt"
或者直接在tomcat bin 目錄新建setenv.bat setenv.sh
文件內容如下
Windows:set JAVA_OPTS="-Djasypt.encryptor.password=jasypt"
Linux:export JAVA_OPTS="-Djasypt.encryptor.password=jasypt"
程序會默認使用。
2、獲取環境變量
Properties properties = System.getProperties(); Set<Object> objects = properties.keySet(); for (Object object : objects) { System.out.println("key:" + object + "---:" + properties.get(object)); } Map<String, String> getenv = System.getenv(); for (Map.Entry<String, String> entry : getenv.entrySet()) { System.out.println(entry.getKey() + "---:" + entry.getValue()); }
三、核心類庫說明
3.1、加密工具類【以文本text為例】
普通文本實現了如下三種方式
以及參看Strong,AES,只是算法不一致
BasicTextEncryptor→PBEWithMD5AndDES
StrongTextEncryptor→PBEWithMD5AndTripleDES
AES256TextEncryptor→PBEWithHMACSHA512AndAES_256
主要是【參看BasicTextEncryptor】
public final class BasicTextEncryptor implements TextEncryptor { private final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); public BasicTextEncryptor() { this.encryptor.setAlgorithm("PBEWithMD5AndDES"); } public void setPassword(String password) { this.encryptor.setPassword(password); } public void setPasswordCharArray(char[] password) { this.encryptor.setPasswordCharArray(password); } public String encrypt(String message) { return this.encryptor.encrypt(message); } public String decrypt(String encryptedMessage) { return this.encryptor.decrypt(encryptedMessage); } }
更多pbe算法可以參看:java-信息安全(三)-PBE加密算法
參看上述工具類編寫方式,以及上述PBE加密算法,編寫:PBEWITHSHA1ANDRC4_128 工具類:RC128TextEncryptor

public class RC128TextEncryptor implements TextEncryptor { private final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); public RC128TextEncryptor() { this.encryptor.setAlgorithm("PBEWITHSHA1ANDRC4_128"); } public void setPassword(String password) { this.encryptor.setPassword(password); } public void setPasswordCharArray(char[] password) { this.encryptor.setPasswordCharArray(password); } public String encrypt(String message) { return this.encryptor.encrypt(message); } public String decrypt(String encryptedMessage) { return this.encryptor.decrypt(encryptedMessage); } }
測試:

public class RC128TextEncryptorTest { RC128TextEncryptor textEncryptor; @Before public void setUp() { textEncryptor = new RC128TextEncryptor(); textEncryptor.setPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7"); } @Test public void encrypt() { // 加密 System.out.println(textEncryptor.encrypt("root@1234")); //zjhmIP38jmvob56qyNevHjs= //iMX2aR70CkLGdtlAdhe2XKI= } @Test public void decyptPwd() { // 解密 // root@1234 System.out.println(textEncryptor.decrypt("zjhmIP38jmvob56qyNevHjs=")); // root@1234 System.out.println(textEncryptor.decrypt("iMX2aR70CkLGdtlAdhe2XKI=")); } }
綜上所述,所有算法核心是調用:StandardPBEStringEncryptor
3.2、StandardPBEStringEncryptor說明

public final class StandardPBEStringEncryptor implements PBEStringCleanablePasswordEncryptor { private static final String MESSAGE_CHARSET = "UTF-8"; private static final String ENCRYPTED_MESSAGE_CHARSET = "US-ASCII"; public static final String DEFAULT_STRING_OUTPUT_TYPE = "base64"; private StringPBEConfig stringPBEConfig = null; private String stringOutputType = "base64"; private boolean stringOutputTypeBase64 = true; private boolean stringOutputTypeSet = false; private final StandardPBEByteEncryptor byteEncryptor; private final Base64 base64; public StandardPBEStringEncryptor() { this.byteEncryptor = new StandardPBEByteEncryptor(); this.base64 = new Base64(); } private StandardPBEStringEncryptor(StandardPBEByteEncryptor standardPBEByteEncryptor) { this.byteEncryptor = standardPBEByteEncryptor; this.base64 = new Base64(); } public synchronized void setConfig(PBEConfig config) { this.byteEncryptor.setConfig(config); if (config != null && config instanceof StringPBEConfig) { this.stringPBEConfig = (StringPBEConfig)config; } } public void setAlgorithm(String algorithm) { this.byteEncryptor.setAlgorithm(algorithm); } public void setPassword(String password) { this.byteEncryptor.setPassword(password); } public void setPasswordCharArray(char[] password) { this.byteEncryptor.setPasswordCharArray(password); } public void setKeyObtentionIterations(int keyObtentionIterations) { this.byteEncryptor.setKeyObtentionIterations(keyObtentionIterations); } public void setSaltGenerator(SaltGenerator saltGenerator) { this.byteEncryptor.setSaltGenerator(saltGenerator); } public void setIvGenerator(IvGenerator ivGenerator) { this.byteEncryptor.setIvGenerator(ivGenerator); } public void setProviderName(String providerName) { this.byteEncryptor.setProviderName(providerName); } public void setProvider(Provider provider) { this.byteEncryptor.setProvider(provider); } public synchronized void setStringOutputType(String stringOutputType) { CommonUtils.validateNotEmpty(stringOutputType, "String output type cannot be set empty"); if (this.isInitialized()) { throw new AlreadyInitializedException(); } else { this.stringOutputType = CommonUtils.getStandardStringOutputType(stringOutputType); this.stringOutputTypeSet = true; } } synchronized StandardPBEStringEncryptor[] cloneAndInitializeEncryptor(int size) { StandardPBEByteEncryptor[] byteEncryptorClones = this.byteEncryptor.cloneAndInitializeEncryptor(size); this.initializeSpecifics(); StandardPBEStringEncryptor[] clones = new StandardPBEStringEncryptor[size]; clones[0] = this; for(int i = 1; i < size; ++i) { clones[i] = new StandardPBEStringEncryptor(byteEncryptorClones[i]); if (CommonUtils.isNotEmpty(this.stringOutputType)) { clones[i].setStringOutputType(this.stringOutputType); } } return clones; } public boolean isInitialized() { return this.byteEncryptor.isInitialized(); } public synchronized void initialize() { if (!this.isInitialized()) { this.initializeSpecifics(); this.byteEncryptor.initialize(); } } private void initializeSpecifics() { if (this.stringPBEConfig != null) { String configStringOutputType = this.stringPBEConfig.getStringOutputType(); this.stringOutputType = !this.stringOutputTypeSet && configStringOutputType != null ? configStringOutputType : this.stringOutputType; } this.stringOutputTypeBase64 = "base64".equalsIgnoreCase(this.stringOutputType); } public String encrypt(String message) { if (message == null) { return null; } else { if (!this.isInitialized()) { this.initialize(); } try { byte[] messageBytes = message.getBytes("UTF-8"); byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes); String result = null; if (this.stringOutputTypeBase64) { encryptedMessage = this.base64.encode(encryptedMessage); result = new String(encryptedMessage, "US-ASCII"); } else { result = CommonUtils.toHexadecimal(encryptedMessage); } return result; } catch (EncryptionInitializationException var5) { throw var5; } catch (EncryptionOperationNotPossibleException var6) { throw var6; } catch (Exception var7) { throw new EncryptionOperationNotPossibleException(); } } } public String decrypt(String encryptedMessage) { if (encryptedMessage == null) { return null; } else { if (!this.isInitialized()) { this.initialize(); } try { byte[] encryptedMessageBytes = null; byte[] encryptedMessageBytes; if (this.stringOutputTypeBase64) { encryptedMessageBytes = encryptedMessage.getBytes("US-ASCII"); encryptedMessageBytes = this.base64.decode(encryptedMessageBytes); } else { encryptedMessageBytes = CommonUtils.fromHexadecimal(encryptedMessage); } byte[] message = this.byteEncryptor.decrypt(encryptedMessageBytes); return new String(message, "UTF-8"); } catch (EncryptionInitializationException var4) { throw var4; } catch (EncryptionOperationNotPossibleException var5) { throw var5; } catch (Exception var6) { throw new EncryptionOperationNotPossibleException(); } } } }
這里涉及了具體加密解密實現邏輯。
直接初始化調用

public class StandardPBEStringEncryptorTest { StandardPBEStringEncryptor textEncryptor; @Before public void setUp() { textEncryptor = new StandardPBEStringEncryptor(); // textEncryptor.setAlgorithm("");//自行指定 textEncryptor.setPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7"); } @Test public void encrypt() { // 加密 System.out.println(textEncryptor.encrypt("root@1234")); //Han0rFt6K2jhvrK5swPpD/ctoUMPckIO //upkr4Rc6bhmpUXhdRoT9qqkhiSfEhTvS } @Test public void decyptPwd() { // 解密 // root@1234 System.out.println(textEncryptor.decrypt("Han0rFt6K2jhvrK5swPpD/ctoUMPckIO")); // root@1234 System.out.println(textEncryptor.decrypt("upkr4Rc6bhmpUXhdRoT9qqkhiSfEhTvS")); } }
3.3、上述2.3使用池化方式