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区别