文档章节

扩展PropertyPlaceholderConfigurer对prop文件中的属性加密(修正1)

NoahX
 NoahX
发布于 2013/09/24 02:41
字数 2770
阅读 4855
收藏 140

一、背景

处于安全考虑需要对.properties中的数据库用户名与密码等敏感数据进行加密。项目中使用了Spring3框架统一加载属性文件,所以最好可以干扰这个加载过程来实现对.properties文件中的部分属性进行加密。

属性文件中的属性最初始时敏感属性值可以为明文,程序第一次执行后自动加密明文为密文。

修正1:

修正了一个小bug,当属性值中包含“=”号时会被截断。但还是没有完全按Java Properties标准进行实现(没考虑“:”、"\"等情况)。

二、问题分析

  1. 扩展PropertyPlaceholderConfigurer最好的方式就是编写一个继承该类的子类。
  2. 外部设置locations时,记录全部locations信息,为加密文件保留属性文件列表。重写setLocations与setLocation方法(在父类中locations私有)
  3. 寻找一个读取属性文件属性的环节,检测敏感属性加密情况。对有已有加密特征的敏感属性进行解密。重写convertProperty方法来实现。
  4. 属性文件第一次加载完毕后,立即对属性文件中的明文信息进行加密。重写postProcessBeanFactory方式来实现。

三、程序开发

1、目录结构

注:aes包中为AES加密工具类,可以根据加密习惯自行修改

2、EncryptPropertyPlaceholderConfigurer(详见注释)

package org.noahx.spring.propencrypt;

import org.noahx.spring.propencrypt.aes.AesUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * User: noah
 * Date: 9/16/13
 * Time: 10:36 AM
 * To change this template use File | Settings | File Templates.
 */
public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    private static final String SEC_KEY = "@^_^123aBcZ*";    //主密钥
    private static final String ENCRYPTED_PREFIX = "Encrypted:{";
    private static final String ENCRYPTED_SUFFIX = "}";
    private static Pattern encryptedPattern = Pattern.compile("Encrypted:\\{((\\w|\\-)*)\\}");  //加密属性特征正则

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private Set<String> encryptedProps = Collections.emptySet();

    public void setEncryptedProps(Set<String> encryptedProps) {
        this.encryptedProps = encryptedProps;
    }

    @Override
    protected String convertProperty(String propertyName, String propertyValue) {

        if (encryptedProps.contains(propertyName)) { //如果在加密属性名单中发现该属性
            final Matcher matcher = encryptedPattern.matcher(propertyValue);  //判断该属性是否已经加密
            if (matcher.matches()) {      //已经加密,进行解密
                String encryptedString = matcher.group(1);    //获得加密值
                String decryptedPropValue = AesUtils.decrypt(propertyName + SEC_KEY, encryptedString);  //调用AES进行解密,SEC_KEY与属性名联合做密钥更安全

                if (decryptedPropValue != null) {  //!=null说明正常
                    propertyValue = decryptedPropValue; //设置解决后的值
                } else {//说明解密失败
                    logger.error("Decrypt " + propertyName + "=" + propertyValue + " error!");
                }
            }
        }

        return super.convertProperty(propertyName, propertyValue);  //将处理过的值传给父类继续处理
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        super.postProcessBeanFactory(beanFactory);    //正常执行属性文件加载

        for (Resource location : locations) {    //加载完后,遍历location,对properties进行加密

            try {
                final File file = location.getFile();
                if (file.isFile()) {  //如果是一个普通文件

                    if (file.canWrite()) { //如果有写权限
                        encrypt(file);   //调用文件加密方法
                    } else {
                        if (logger.isWarnEnabled()) {
                            logger.warn("File '" + location + "' can not be write!");
                        }
                    }

                } else {
                    if (logger.isWarnEnabled()) {
                        logger.warn("File '" + location + "' is not a normal file!");
                    }
                }

            } catch (IOException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("File '" + location + "' is not a normal file!");
                }
            }
        }

    }

    private boolean isBlank(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(str.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    private boolean isNotBlank(String str) {
        return !isBlank(str);
    }


    /**
     * 属性文件加密方法
     *
     * @param file
     */
    private void encrypt(File file) {

        List<String> outputLine = new ArrayList<String>();   //定义输出行缓存

        boolean doEncrypt = false;     //是否加密属性文件标识


        BufferedReader bufferedReader = null;
        try {

            bufferedReader = new BufferedReader(new FileReader(file));

            String line = null;
            do {

                line = bufferedReader.readLine(); //按行读取属性文件
                if (line != null) { //判断是否文件结束
                    if (isNotBlank(line)) {   //是否为空行
                        line = line.trim();    //取掉左右空格
                        if (!line.startsWith("#")) {//如果是非注释行
                            String[] lineParts = line.split("=");  //将属性名与值分离
                            String key = lineParts[0];       // 属性名
                            String value = lineParts[1];      //属性值
                            if (key != null && value != null) {
                                if (encryptedProps.contains(key)) { //发现是加密属性
                                    final Matcher matcher = encryptedPattern.matcher(value);
                                    if (!matcher.matches()) { //如果是非加密格式,则`进行加密

                                        value = ENCRYPTED_PREFIX + AesUtils.encrypt(key + SEC_KEY, value) + ENCRYPTED_SUFFIX;   //进行加密,SEC_KEY与属性名联合做密钥更安全

                                        line = key + "=" + value;  //生成新一行的加密串

                                        doEncrypt = true;    //设置加密属性文件标识

                                        if (logger.isDebugEnabled()) {
                                            logger.debug("encrypt property:" + key);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    outputLine.add(line);
                }

            } while (line != null);


        } catch (FileNotFoundException e) {
            logger.error(e.getMessage(), e);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }

        if (doEncrypt) {      //判断属性文件加密标识
            BufferedWriter bufferedWriter = null;
            File tmpFile = null;
            try {
                tmpFile = File.createTempFile(file.getName(), null, file.getParentFile());   //创建临时文件

                if (logger.isDebugEnabled()) {
                    logger.debug("Create tmp file '" + tmpFile.getAbsolutePath() + "'.");
                }

                bufferedWriter = new BufferedWriter(new FileWriter(tmpFile));

                final Iterator<String> iterator = outputLine.iterator();
                while (iterator.hasNext()) {                           //将加密后内容写入临时文件
                    bufferedWriter.write(iterator.next());
                    if (iterator.hasNext()) {
                        bufferedWriter.newLine();
                    }
                }

                bufferedWriter.flush();
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            } finally {
                if (bufferedWriter != null) {
                    try {
                        bufferedWriter.close();
                    } catch (IOException e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }

            File backupFile = new File(file.getAbsoluteFile() + "_" + System.currentTimeMillis());  //准备备份文件名

            //以下为备份,异常恢复机制
            if (!file.renameTo(backupFile)) {   //重命名原properties文件,(备份)
                logger.error("Could not encrypt the file '" + file.getAbsoluteFile() + "'! Backup the file failed!");
                tmpFile.delete(); //删除临时文件
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Backup the file '" + backupFile.getAbsolutePath() + "'.");
                }

                if (!tmpFile.renameTo(file)) {   //临时文件重命名失败 (加密文件替换原失败)
                    logger.error("Could not encrypt the file '" + file.getAbsoluteFile() + "'! Rename the tmp file failed!");

                    if (backupFile.renameTo(file)) {   //恢复备份
                        if (logger.isInfoEnabled()) {
                            logger.info("Restore the backup, success.");
                        }
                    } else {
                        logger.error("Restore the backup, failed!");
                    }
                } else {  //(加密文件替换原成功)

                    if (logger.isDebugEnabled()) {
                        logger.debug("Rename the file '" + tmpFile.getAbsolutePath() + "' -> '" + file.getAbsoluteFile() + "'.");
                    }

                    boolean dBackup = backupFile.delete();//删除备份文件

                    if (logger.isDebugEnabled()) {
                        logger.debug("Delete the backup '" + backupFile.getAbsolutePath() + "'.(" + dBackup + ")");
                    }
                }
            }


        }

    }

    protected Resource[] locations;

    @Override
    public void setLocations(Resource[] locations) {   //由于location是父类私有,所以需要记录到本类的locations中
        super.setLocations(locations);
        this.locations = locations;
    }

    @Override
    public void setLocation(Resource location) {   //由于location是父类私有,所以需要记录到本类的locations中
        super.setLocation(location);
        this.locations = new Resource[]{location};
    }
}

2、EncryptPropertyPlaceholderConfigurer(详见注释)(修正1)

package org.noahx.spring.propencrypt;

import org.noahx.spring.propencrypt.aes.AesUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * User: noah
 * Date: 9/16/13
 * Time: 10:36 AM
 * To change this template use File | Settings | File Templates.
 */
public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    private static final String SEC_KEY = "@^_^123aBcZ*";    //主密钥
    private static final String ENCRYPTED_PREFIX = "Encrypted:{";
    private static final String ENCRYPTED_SUFFIX = "}";
    private static Pattern encryptedPattern = Pattern.compile("Encrypted:\\{((\\w|\\-)*)\\}");  //加密属性特征正则

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private Set<String> encryptedProps = Collections.emptySet();

    public void setEncryptedProps(Set<String> encryptedProps) {
        this.encryptedProps = encryptedProps;
    }

    @Override
    protected String convertProperty(String propertyName, String propertyValue) {

        if (encryptedProps.contains(propertyName)) { //如果在加密属性名单中发现该属性
            final Matcher matcher = encryptedPattern.matcher(propertyValue);  //判断该属性是否已经加密
            if (matcher.matches()) {      //已经加密,进行解密
                String encryptedString = matcher.group(1);    //获得加密值
                String decryptedPropValue = AesUtils.decrypt(propertyName + SEC_KEY, encryptedString);  //调用AES进行解密,SEC_KEY与属性名联合做密钥更安全

                if (decryptedPropValue != null) {  //!=null说明正常
                    propertyValue = decryptedPropValue; //设置解决后的值
                } else {//说明解密失败
                    logger.error("Decrypt " + propertyName + "=" + propertyValue + " error!");
                }
            }
        }

        return super.convertProperty(propertyName, propertyValue);  //将处理过的值传给父类继续处理
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        super.postProcessBeanFactory(beanFactory);    //正常执行属性文件加载

        for (Resource location : locations) {    //加载完后,遍历location,对properties进行加密

            try {
                final File file = location.getFile();
                if (file.isFile()) {  //如果是一个普通文件

                    if (file.canWrite()) { //如果有写权限
                        encrypt(file);   //调用文件加密方法
                    } else {
                        if (logger.isWarnEnabled()) {
                            logger.warn("File '" + location + "' can not be write!");
                        }
                    }

                } else {
                    if (logger.isWarnEnabled()) {
                        logger.warn("File '" + location + "' is not a normal file!");
                    }
                }

            } catch (IOException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("File '" + location + "' is not a normal file!");
                }
            }
        }

    }

    private boolean isBlank(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(str.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    private boolean isNotBlank(String str) {
        return !isBlank(str);
    }


    /**
     * 属性文件加密方法
     *
     * @param file
     */
    private void encrypt(File file) {

        List<String> outputLine = new ArrayList<String>();   //定义输出行缓存

        boolean doEncrypt = false;     //是否加密属性文件标识


        BufferedReader bufferedReader = null;
        try {

            bufferedReader = new BufferedReader(new FileReader(file));

            String line = null;
            do {

                line = bufferedReader.readLine(); //按行读取属性文件
                if (line != null) { //判断是否文件结束
                    if (isNotBlank(line)) {   //是否为空行
                        line = line.trim();    //取掉左右空格
                        if (!line.startsWith("#")) {//如果是非注释行
//                            String[] lineParts = line.split("=");  //将属性名与值分离
//                            String key = lineParts[0];       // 属性名
//                            String value = lineParts[1];      //属性值
                            int eIndex = line.indexOf("=");  //将属性名与值分离(修正1)
                            String key = line.substring(0,eIndex);       // 属性名
                            String value = line.substring(eIndex+1);      //属性值
                            if (key != null && value != null) {
                                if (encryptedProps.contains(key)) { //发现是加密属性
                                    final Matcher matcher = encryptedPattern.matcher(value);
                                    if (!matcher.matches()) { //如果是非加密格式,则`进行加密

                                        value = ENCRYPTED_PREFIX + AesUtils.encrypt(key + SEC_KEY, value) + ENCRYPTED_SUFFIX;   //进行加密,SEC_KEY与属性名联合做密钥更安全

                                        line = key + "=" + value;  //生成新一行的加密串

                                        doEncrypt = true;    //设置加密属性文件标识

                                        if (logger.isDebugEnabled()) {
                                            logger.debug("encrypt property:" + key);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    outputLine.add(line);
                }

            } while (line != null);


        } catch (FileNotFoundException e) {
            logger.error(e.getMessage(), e);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }

        if (doEncrypt) {      //判断属性文件加密标识
            BufferedWriter bufferedWriter = null;
            File tmpFile = null;
            try {
                tmpFile = File.createTempFile(file.getName(), null, file.getParentFile());   //创建临时文件

                if (logger.isDebugEnabled()) {
                    logger.debug("Create tmp file '" + tmpFile.getAbsolutePath() + "'.");
                }

                bufferedWriter = new BufferedWriter(new FileWriter(tmpFile));

                final Iterator<String> iterator = outputLine.iterator();
                while (iterator.hasNext()) {                           //将加密后内容写入临时文件
                    bufferedWriter.write(iterator.next());
                    if (iterator.hasNext()) {
                        bufferedWriter.newLine();
                    }
                }

                bufferedWriter.flush();
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            } finally {
                if (bufferedWriter != null) {
                    try {
                        bufferedWriter.close();
                    } catch (IOException e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }

            File backupFile = new File(file.getAbsoluteFile() + "_" + System.currentTimeMillis());  //准备备份文件名

            //以下为备份,异常恢复机制
            if (!file.renameTo(backupFile)) {   //重命名原properties文件,(备份)
                logger.error("Could not encrypt the file '" + file.getAbsoluteFile() + "'! Backup the file failed!");
                tmpFile.delete(); //删除临时文件
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Backup the file '" + backupFile.getAbsolutePath() + "'.");
                }

                if (!tmpFile.renameTo(file)) {   //临时文件重命名失败 (加密文件替换原失败)
                    logger.error("Could not encrypt the file '" + file.getAbsoluteFile() + "'! Rename the tmp file failed!");

                    if (backupFile.renameTo(file)) {   //恢复备份
                        if (logger.isInfoEnabled()) {
                            logger.info("Restore the backup, success.");
                        }
                    } else {
                        logger.error("Restore the backup, failed!");
                    }
                } else {  //(加密文件替换原成功)

                    if (logger.isDebugEnabled()) {
                        logger.debug("Rename the file '" + tmpFile.getAbsolutePath() + "' -> '" + file.getAbsoluteFile() + "'.");
                    }

                    boolean dBackup = backupFile.delete();//删除备份文件

                    if (logger.isDebugEnabled()) {
                        logger.debug("Delete the backup '" + backupFile.getAbsolutePath() + "'.(" + dBackup + ")");
                    }
                }
            }


        }

    }

    protected Resource[] locations;

    @Override
    public void setLocations(Resource[] locations) {   //由于location是父类私有,所以需要记录到本类的locations中
        super.setLocations(locations);
        this.locations = locations;
    }

    @Override
    public void setLocation(Resource location) {   //由于location是父类私有,所以需要记录到本类的locations中
        super.setLocation(location);
        this.locations = new Resource[]{location};
    }
}
注意:134行的变化,把split换为了indexof

3、spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="/WEB-INF/spring/spring.properties"/>

    <!--对spring.properties配置文件中的指定属性进行加密-->
    <bean id="encryptPropertyPlaceholderConfigurer"
          class="org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>/WEB-INF/spring/spring.properties</value>
            </list>
        </property>
        <property name="encryptedProps">
            <set>
                <value>db.jdbc.username</value>
                <value>db.jdbc.password</value>
                <value>db.jdbc.url</value>
            </set>
        </property>
    </bean>

</beans>

四、运行效果

1、日志

[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - encrypt property:db.jdbc.url
[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - encrypt property:db.jdbc.username
[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - encrypt property:db.jdbc.password
[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - Create tmp file '/nautilus/workspaces/idea/spring-prop-encrypt/target/spring-prop-encrypt-1.0-SNAPSHOT/WEB-INF/spring/spring.properties2420183175827237221.tmp'.
[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - Backup the file '/nautilus/workspaces/idea/spring-prop-encrypt/target/spring-prop-encrypt-1.0-SNAPSHOT/WEB-INF/spring/spring.properties_1379959755837'.
[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - Rename the file '/nautilus/workspaces/idea/spring-prop-encrypt/target/spring-prop-encrypt-1.0-SNAPSHOT/WEB-INF/spring/spring.properties2420183175827237221.tmp' -> '/nautilus/workspaces/idea/spring-prop-encrypt/target/spring-prop-encrypt-1.0-SNAPSHOT/WEB-INF/spring/spring.properties'.
[RMI TCP Connection(2)-127.0.0.1] DEBUG org.noahx.spring.propencrypt.EncryptPropertyPlaceholderConfigurer - Delete the backup '/nautilus/workspaces/idea/spring-prop-encrypt/target/spring-prop-encrypt-1.0-SNAPSHOT/WEB-INF/spring/spring.properties_1379959755837'.(true)

2、原属性文件

db.jdbc.driver=com.mysql.jdbc.Driver
db.jdbc.url=jdbc:mysql://localhost:3306/noah?useUnicode=true&characterEncoding=utf8
db.jdbc.username=noah
db.jdbc.password=noah

3、加密后的文件

db.jdbc.driver=com.mysql.jdbc.Driver
db.jdbc.url=Encrypted:{e5ShuhQjzDZrkqoVdaO6XNQrTqCPIWv8i_VR4zaK28BrmWS_ocagv3weYNdr0WwI}
db.jdbc.username=Encrypted:{z5aneQi_h4mk4LEqhjZU-A}
db.jdbc.password=Encrypted:{v09a0SrOGbw-_DxZKieu5w}
注:因为密钥与属性名有关,所以相同值加密后的内容也不同,而且不能互换值。


五、源码下载

附件地址:http://sdrv.ms/1eguILi

六、总结

在成熟加密框架中jasypt(http://www.jasypt.org/)很不错,包含了spring,hibernate等等加密。试用了一些功能后感觉并不太适合我的需要。

加密的安全性是相对的,没有绝对安全的东西。如果有人反编译了加密程序获得了加密解密算法也属正常。希望大家不要因为是否绝对安全而讨论不休。

如果追求更高级别的加密可以考虑混淆class的同时对class文件本身进行加密,改写默认的classloader加载加密class(调用本地核心加密程序,非Java)。

© 著作权归作者所有

NoahX
粉丝 136
博文 59
码字总数 48419
作品 0
海淀
程序员
私信 提问
加载中

评论(18)

NoahX
NoahX 博主

引用来自“刀哥”的评论

以前也这么干过,用的rsa加密。但是有个问题,密码加密了,但是密钥还是要提供的,不然程序也无法获取密码。那如果有密钥了,加密也没什么用了啊。
有更好的办法吗?

只要解密是自动进行的过程不需要人为干预就会一直存在你说的问题。如果想彻底解决需要系统在启动时人工输入密码或通过其它人工手段(ukey,指纹,声纹)。
也不是完全没有作用,总比直接把敏感信息暴露在文件中好点吧。
刀哥
刀哥
以前也这么干过,用的rsa加密。但是有个问题,密码加密了,但是密钥还是要提供的,不然程序也无法获取密码。那如果有密钥了,加密也没什么用了啊。
有更好的办法吗?
hillshills
hillshills
既然对安全有要求,还保存在文件?
youngjdong
youngjdong
# 开发环境下不导入his数据 -- 被认为是配置信息,程序出错了
n
notrap
通过容器使用JDBC资源的话,就不存在明文密码了吧
黄亿华
黄亿华

引用来自“肖国颖”的评论

引用来自“黄亿华”的评论

引用来自“肖国颖”的评论

引用来自“黄亿华”的评论

话说这样加密,跑一遍程序也出来了吧?

摘要(Digest)类认证(如:SHA1,MD5)是可以跑程序进行对撞来获得等效的密码。AES需要密钥来反解为明文,如果解密程序(密钥安全、加入算法安全)足够安全的情况下(比如只获得了.properties文件),对撞的方式就不太起作用。如果算法泄漏,就可以采用穷举密钥的方式暴力破解。如果密钥也泄露,那只能恭喜破解成功了。

我是说跑一遍原始解密程序不是破解=.=
因为项目源码(包含解密程序)一般都是跟properties文件在一起的吧?

哦,理解错了,那是可以破。所以需要对加密程序混淆或增加其它反编译手段。

我觉得数据库账号密码这种还是放到远程服务里,程序动态去请求比较好。这样一来源码泄露没有任何影响,二来可以隔离环境和代码。
qiaoning13256
qiaoning13256

引用来自“雅典娜拉”的评论

请教楼主用的是啥编辑器啊 真是好看

这个是网页css显示的效果
NoahX
NoahX 博主

引用来自“黄亿华”的评论

引用来自“肖国颖”的评论

引用来自“黄亿华”的评论

话说这样加密,跑一遍程序也出来了吧?

摘要(Digest)类认证(如:SHA1,MD5)是可以跑程序进行对撞来获得等效的密码。AES需要密钥来反解为明文,如果解密程序(密钥安全、加入算法安全)足够安全的情况下(比如只获得了.properties文件),对撞的方式就不太起作用。如果算法泄漏,就可以采用穷举密钥的方式暴力破解。如果密钥也泄露,那只能恭喜破解成功了。

我是说跑一遍原始解密程序不是破解=.=
因为项目源码(包含解密程序)一般都是跟properties文件在一起的吧?

哦,理解错了,那是可以破。所以需要对加密程序混淆或增加其它反编译手段。
黄亿华
黄亿华

引用来自“肖国颖”的评论

引用来自“黄亿华”的评论

话说这样加密,跑一遍程序也出来了吧?

摘要(Digest)类认证(如:SHA1,MD5)是可以跑程序进行对撞来获得等效的密码。AES需要密钥来反解为明文,如果解密程序(密钥安全、加入算法安全)足够安全的情况下(比如只获得了.properties文件),对撞的方式就不太起作用。如果算法泄漏,就可以采用穷举密钥的方式暴力破解。如果密钥也泄露,那只能恭喜破解成功了。

我是说跑一遍原始解密程序不是破解=.=
因为项目源码(包含解密程序)一般都是跟properties文件在一起的吧?
NoahX
NoahX 博主

引用来自“黄亿华”的评论

话说这样加密,跑一遍程序也出来了吧?

摘要(Digest)类认证(如:SHA1,MD5)是可以跑程序进行对撞来获得等效的密码。AES需要密钥来反解为明文,如果解密程序(密钥安全、加入算法安全)足够安全的情况下(比如只获得了.properties文件),对撞的方式就不太起作用。如果算法泄漏,就可以采用穷举密钥的方式暴力破解。如果密钥也泄露,那只能恭喜破解成功了。
扩展PropertyPlaceholderConfigurer

扩展PropertyPlaceholderConfigurer 要Spring配置时,将一些重要的信息独立到属性文件中是比较常见的做法,Spring只支持明文存放的属性文件,在某些场合下,我们可以希望对属性文件加密保存,...

blooms
2012/10/27
440
0
说说在 Spring 中如何引用外部属性文件

进行数据源或者 FTP 服务器等资源配置时,我们可以将这些配置信息放到一个独立的外部属性文件中,并在 Spring 配置文件中通过形如 、 的占位符方式来引用属性文件中的属性项 。 这种方式的配...

deniro
2018/05/22
0
0
spring源码学习之:项目公共配置项解决方案

一:项目中有一些key,value的简单配置 org.apache.commons.configuration.DatabaseConfiguration可以轻松解决 二:配置项目的xml中bean 1 2...

无信不立
2017/03/06
0
0
Spring的PropertyPlaceholderConfigurer应用

Spring 利用PropertyPlaceholderConfigurer占位符 1. PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。PropertyPlaceholderCo...

java远
2013/11/28
2.1K
0
spring中context:property-placeholder/元素

1.有些参数在某些阶段中是常量 比如:a、在开发阶段我们连接数据库时的连接url,username,password,driverClass等 b、分布式应用中client端访问server端所用的server地址,port,service等...

Megan_zhou
2014/09/18
130
0

没有更多内容

加载失败,请刷新页面

加载更多

Gradle 的项目导入到 IntelliJ 后子项目源代码不能导入

在一个 Gradle 项目中,有若干子项目。 当 Gradle 到如后,子项目不能被 IntelliJ 识别代码。 如下图的这个代码就没有被自动识别。 这个有可能是因为你的这个子项目没有被添加到父项目中。 ...

honeymoose
20分钟前
3
0
苹果cms下载地址及模板地址

https://github.com/magicblack/maccms10 程序下载: https://www.lanzous.com/b204882 教程下载: https://www.lanzous.com/b256378 模板下载: https://www.lanzous.com/b355667 插件下载:......

chenhongjiang
21分钟前
3
0
Java中使用HttpPost上传文件以及HttpGet进行API请求(包含HttpPost上传文件)

一、HttpPost上传文件 public static String getSuffix(final MultipartFile file){ if(file == null || file.getSize() == 0){ return null; } String......

codeobj
22分钟前
3
0
在Word中怎样批量删除空行,这些点主要注意

在工作中经常接触的办公软件就是Word了,熟练使用Word中的技能是准时下班的保证。这就要求我们对Word中的各项技能都熟练于心,很多朋友诉苦Word中的排版不熟悉,每次写一篇文章排版都要花费很...

干货趣分享
28分钟前
2
0
终端-Linux命令之非交互SSH密码验证-Sshpass

> Sshpass是使用SSH所谓的“交互式键盘密码身份验证”以非交互方式执行密码身份验证的工具 通俗来说就是 使用ssh密码登录 是需要在连接时手动输入密码的,没办法明文连接,如下图,需要交互的...

极客收藏夹
31分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部