java-信息安全(十九)加密工具Jasypt

一、概述

  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结合

  配置文件参看:005-Spring Boot配置分析-配置文件application、Environment、PropertySource、@Value、EnvironmentPostProcessor、Profiles

  源码地址: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);
    }
}
View Code

 

测试:

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="));
    }
}
View Code

 

综上所述,所有算法核心是调用: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();
            }
        }
    }
}
View Code

 

这里涉及了具体加密解密实现逻辑。

直接初始化调用

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"));
    }
}
View Code

 

3.3、上述2.3使用池化方式

 

 
posted @ 2020-01-22 18:30  bjlhx15  阅读(12720)  评论(0编辑  收藏  举报
Copyright ©2011~2020 JD-李宏旭