SHA和AES加密+GUI Swing写的一个本地运行和保存的密码管理小工具

Posted 符华-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SHA和AES加密+GUI Swing写的一个本地运行和保存的密码管理小工具相关的知识,希望对你有一定的参考价值。

目录

通过SHA和AES加密方式,用Swing写的窗口界面

主要实现了登录、加密、解密、列表展示。其中密码加密后密文是以txt文件的形式保存到本地的。

我们来先看看效果。

效果







项目结构



功能

看完了效果和结构图,我们再来说下功能。

1、登录

在第一次使用这个程序时(通过有没有 密码管理器.txt 文件来判断是不是第一次使用),要先设置登录密码,以后用这个程序都是要用这个密码登录,并且加密解密也是用这个密码作为密钥的。

登录密码是用sha256进行加密(不可逆),密文保存到了 密码管理器.txt,这个文件设置了只读和隐藏。后续通过输入的密码用同样方式加密得到密文和这个txt的密文进行验证,通过了才能进入到主页,没通过直接结束程序。



2、加密

加密是会用到登录密码的,以登录密码作为密钥加密。得到的密文保存到 加密密码.txt。如果程序/网站名在txt已经存在了,则会把原密文替换成新的密文(修改密码)。比如微信,一开始的密码是123456,后面又输入了微信和新的密码点击加密,在txt中原本微信对应的密文就会变成新密码的密文。



3、解密

解密同样会用到登录密码。输入密文点击解密时,要再次验证登录密码,这是防止你自己登录进来后,程序还在运行,别人直接拿你的密文去解密。 如果密码错了也是直接退出程序,正确则进行解密。




解密用登录密码作为密钥,这是防止别人拿到了我的密文,ta去使用这个程序,设置的登录密码也是ta自己的(ta不知道我的登录密码),然后输入我的密文,点击解密,输入ta的登录密码后校验通过,导致我的密文被解密出来了。 用密码作为密钥的话,只要不知道我的登录密码,即使ta能用这个程序,知道我的密文,也不可能解密出来。 是不是这么个道理😎



4、列表

这个只是展示 加密密码.txt 的内容的。本来一开始是想除了展示,还能直接在这个列表进行解密(过程和上面那个一样)和修改密码。后面发现JTable这个组件太难搞了,光是一个展示页面,要让它在tab面板里面显示出来、设置表格样式这些就花了我不少时间(对swing真不会用),要让它每一行显示两个按钮,并且是可以点击的按钮,不会搞,想想就算了(不知道以后有没有精力加上去)😢,还是只做展示用吧,要修改/解密去加密、解密面板操作吧。

代码

1、先准备好两种加密方式的工具类

SHAUtil

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

/**
 * 密码盐值加密工具类
 */
public class SHAUtil 

    public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";

    /**
     * 盐的长度
     */
    public static final int SALT_BYTE_SIZE = 16;

    /**
     * 生成密文的长度
     */
    public static final int HASH_BIT_SIZE = 256;

    /**
     * 迭代次数
     */
    public static final int PBKDF2_ITERATIONS = 1000;

    /**
     * 验证输入的password是否正确
     * @param attemptedPassword 待验证的password
     * @param encryptedPassword 密文
     * @param salt 盐值
     */
    public static boolean authenticate(String attemptedPassword, String encryptedPassword, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException 
        // 用同样的盐值对用户输入的password进行加密
        String encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);
        // 把加密后的密文和原密文进行比較,同样则验证成功。否则失败
        return encryptedAttemptedPassword.equals(encryptedPassword);
    

    /**
     * 生成密文
     * @param password 明文password
     * @param salt 盐值
     */
    public static String getEncryptedPassword(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException 
        KeySpec spec = new PBEKeySpec(password.toCharArray(), fromHex(salt), PBKDF2_ITERATIONS, HASH_BIT_SIZE);
        SecretKeyFactory f = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
        return toHex(f.generateSecret(spec).getEncoded());
    

    /**
     * 通过提供加密的强随机数生成器 生成盐
     */
    public static String generateSalt() throws NoSuchAlgorithmException 
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[SALT_BYTE_SIZE];
        random.nextBytes(salt);
        return toHex(salt);
    

    /**
     * 十六进制字符串转二进制字符串
     * @param hex the hex string
     */
    private static byte[] fromHex(String hex) 
        byte[] binary = new byte[hex.length() / 2];
        for (int i = 0; i < binary.length; i++) 
            binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        
        return binary;
    

    /**
     * 二进制字符串转十六进制字符串
     * @param array the byte array to convert
     */
    private static String toHex(byte[] array) 
        BigInteger bi = new BigInteger(1, array);
        String hex = bi.toString(16);
        int paddingLength = (array.length * 2) - hex.length();
        if (paddingLength > 0) return String.format("%0" + paddingLength + "d", 0) + hex;
        else return hex;
    

AESUtil

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.io.*;
import java.util.Random;

/**
 * AES加密、解密工具类
 */
public class AESUtil 

    /*
     * 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式,key需要为16位。
     * key是aes的一个加密源,也就是通过该key加密后,必须通过该key就进行解密;对数据不是特别敏感的,直接用静态的key即可;
     * 有些场景下数据异常敏感,那么这个key就要动态生成,结合RSA进行加密,,就能做到每次请求,key都不一样,安全程度大大加强;
     * 当然key可以为32,但是要下载两个jdk的jar包
     */
    public static String 默认密钥 = "xcwssythjqruijkg";
    public static String 密钥 = 默认密钥;

    /**
     * 偏移量,随便给一个就可以了,不给也行
     */
    public static String 默认偏移量 = "xcwssythjqruijkg";
    public static String 偏移量 = 默认偏移量;

    /**
     * 分隔符
     */
    public static final String separate = "ZmhzeGZsd2I=";

    public static String 登录密码 = "";
    public static String 密文 = "";

    /**
     * 加密
     * @param str 要加密的字符串
     * @param flag 是否使用默认密钥和偏移量进行加密
     */
    public static String encrypt(String str,boolean flag) 
        int length = 登录密码.length();
        try 
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            if (!flag)
                密钥 = 登录密码 + generateRandom(16-length); // 随机生成密钥
                偏移量 = 登录密码 + generateRandom(16-length); // 获取偏移量
            else 
                密钥 = 登录密码 + 默认密钥.substring(0,16-length);
                偏移量 = 登录密码 + 默认偏移量.substring(0,16-length);
            
            //System.out.println("加密密钥和偏移量:"+密钥+"\\t"+偏移量);
            SecretKeySpec skeySpec = new SecretKeySpec(密钥.getBytes(), "AES");
            IvParameterSpec ivp = new IvParameterSpec(偏移量.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivp);
            byte[] encrypted = cipher.doFinal(str.getBytes("utf-8"));
            return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
        catch (Exception e)
            e.printStackTrace();
        
        return null;
    

    /**
     * 解密
     * @param str 要解密的字符串
     * @param raw 密钥
     * @param ivStr 偏移量
     */
    public static String decrypt(String str,String raw,String ivStr)
        try 
            SecretKeySpec skeySpec = new SecretKeySpec(raw.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(str);// 先用base64解密
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, "utf-8");
            return originalString;
         catch (Exception ex) 
            return null;
        
    

    /**
     * 写入到txt文件
     * @param path   文件路径
     * @param start 内容开头
     * @param content 内容
     */
    public static void 写入内容(String path,String start,String content) 
        BufferedWriter out = null;
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        try 
            File file = new File(path);
            if (!file.exists())
                file.createNewFile();
            
            String str = "";
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            while((str = reader.readLine())!=null)
                if (isNotBlank(start) && str.startsWith(start)) // 替换旧内容
                    sb.append(content).append("\\n");
                else 
                    sb.append(str).append("\\n");
                
            
            str = sb.toString();
            if (!str.contains(content)) // 追加新内容
                sb.append(content).append("\\n");
            
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            out.write(sb.toString());
         catch (Exception e) 
            e.printStackTrace();
         finally 
            try 
                reader.close();
                out.close();
             catch (IOException e) 
                e.printStackTrace();
            
        
    

    public static String 读取登录密码(String path) 
        BufferedReader reader = null;
        String str = "";
        try 
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
            while((str = reader.readLine())!=null)
                break;
            
         catch (Exception e) 
            e.printStackTrace();
         finally 
            try 
                reader.close();
             catch (IOException e) 
                e.printStackTrace();
            
        
        密文 = str;
        return str;
    

    /**
     * 根据指定长度随机生成字符串
     */
    public static String generateRandom(int length) 
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; ++i) 
            int number = random.nextInt(str.length());
            sb.append(str.charAt(number));
        
        return sb.toString();
    

    public static boolean isNotBlank(String str)
        if (str == null || str.length() == 0)
            return false;
        
        return true;
    

    public static boolean isNull(String str)
        if (str == null || str.length() == 0)
            return true;
        
        return false;
    

    /**
     * 提示框
     */
    public static void tips(String str)
        UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 16)));
        JOptionPane.showMessageDialog(null, str);
    


2、登录窗口

import com.util.AESUtil;
import com.util.SHAUtil;
import com.主程序;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

public class 登录 
    public JFrame frame;
    public JPanel panel;
    private JLabel label0;
    private JButton 确定Button;
    private JPasswordField 请输入登录密码PasswordField;

    /**
     * 登录采用SHA256盐值加密
     * @param 调用位置 用于区分是在哪个地方调用显示这个窗口的:1 主程序 2 主页
     */
    public 登录(int 调用位置) 
        String path = System.getProperty("user.dir")+"/密码管理器.txt";
        File file = new File(path)[转] AES,SHA1,DES,RSA,MD5区别

AES,SHA1,DES,RSA,MD5区别[转]

SHA 和 AES 加密有啥区别? [关闭]

JS前端接口加密/解密

AES128_CBC_NoPading加密、sha256withRSA签名

[转]加密算法(DES,AES,RSA,MD5,SHA1,Base64)比较和项目应用