网站首页 > 技术文章 正文
OpenSSL和keytool
先说一下两个重要的工具
- OpenSSL:OpenSSL整个软件包大概可以分成三个主要的功能部分:SSL协议库libssl、应用程序命令工具以及密码算法库libcrypto。它使用标准的文件格式(PEM/CER/CRT/PKCS等)存储密钥和证书信息。
- keytool:是密钥和证书管理工具。它出自于Java体系,它使用KeyStore来管理密钥和证书。
两者都是可以用来生成加密密钥的工具,keytool出自Java体系,它可以直接操作KeyStore,而OpenSSL不支持直接操作KeyStore。实际情况有可能是这样的,使用OpenSSL生成了密钥或证书,然后使用keytool将其导入到KeyStore以便在Java环境中使用。
当然OpenSSL还具备其他功能比如作为SSL的客户端和服务器,这是keytool所不具备的。
对称加密
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。——百度百科
对称加密算法的特点
- 加密和解密使用同样的密钥
- 计算速度快,适用于对大量数据加密处理
- 安全性取决于算法,也取决于密钥的管理,一旦密钥泄漏,数据则暴露无遗
对称加密算法的使用场景
基于上述的特点,在一些需要高效实时传输的加密通讯场景中,比如使用VPN或者代理进行通讯时,可以使用对称加密。另外在同一个系统内部不同模块,比如前后端,从前端输入的敏感信息,可以使用对称加密算法进行加密后将密文传到后端,避免传输过程中明文被截获,因为同系统内部之间密钥管理相对容易,而对于共享密钥有泄漏风险的其他任何场景,则不适合使用对称加密算法进行加密。
常见的对称加密算法
算法 描述 DES(Data Encryption Standard) 数据加密标准,速度较快,适用于加密大量数据 3DES(Triple DES) 基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高 AES(Advanced Encryption Standard) 高级加密标准,速度快,安全级别高,支持128、192、256、512位密钥的加密 Blowfish 速度快且安全,而且没有专利和商业限制。了解更多>>
OpenSSL实现对称加密
OpenSSL> enc --help
usage: enc -ciphername [-AadePp] [-base64] [-bufsize number] [-debug]
[-in file] [-iv IV] [-K key] [-k password]
[-kfile file] [-md digest] [-none] [-nopad] [-nosalt]
[-out file] [-pass arg] [-S salt] [-salt]
-A Process base64 data on one line (requires -a)
-a Perform base64 encoding/decoding (alias -base64)
-bufsize size Specify the buffer size to use for I/O
-d Decrypt the input data
-debug Print debugging information
-e Encrypt the input data (default)
-in file Input file to read from (default stdin)
-iv IV IV to use, specified as a hexadecimal string
-K key Key to use, specified as a hexadecimal string
-md digest Digest to use to create a key from the passphrase
-none Use NULL cipher (no encryption or decryption)
-nopad Disable standard block padding
-out file Output file to write to (default stdout)
-P Print out the salt, key and IV used, then exit
(no encryption or decryption is performed)
-p Print out the salt, key and IV used
-pass source Password source
-S salt Salt to use, specified as a hexadecimal string
-salt Use a salt in the key derivation routines (default)
-v Verbose
命令选项 描述 -in file 被加密文件的全路径 -out file 加密后内容输出的文件路径 -salt 自动插入一个随机数作为文件内容加密,默认选项 -e 加密模式,默认 -d 解密模式,需要与加密算法一致 -a 使用-base64位编码格式,也可使用-base64 -pass source 指定密码的输入方式,共有五种方式:命令行输入(stdin)、文件输入(file)、环境变量输入(var)、文件描述符输入(fd)、标准输入(stdin)。默认是标准输入即从键盘输入
只对文件进行base64编码,而不使用加解密
/*对文件进行base64编码*/
openssl enc -base64 -in plain.txt -out base64.txt
/*对base64格式文件进行解密操作*/
openssl enc -base64 -d -in base64.txt -out plain2.txt
/*使用diff命令查看可知解码前后明文一样*/
diff plain.txt plain2.txt
不同方式的密码输入方式
/*命令行输入,密码123456*/
openssl enc -aes-128-cbc -in plain.txt -out out.txt -pass pass:123456
/*文件输入,密码123456*/
echo 123456 > passwd.txt
openssl enc -aes-128-cbc -in plain.txt -out out.txt -pass file:passwd.txt
/*环境变量输入,密码123456*/
passwd=123456
export passwd
openssl enc -aes-128-cbc -in plain.txt -out out.txt -pass env:passwd
/*从文件描述输入*/
openssl enc -aes-128-cbc -in plain.txt -out out.txt -pass fd:1
/*从标准输入输入*/
openssl enc -aes-128-cbc -in plain.txt -out out.txt -pass stdin
Java实现对称加密
DES
/**
* 生成 DES 算法密钥
* @return byte[]
* @throws Exception
*/
public static byte[] generateDESKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
// must be equal to 56
keyGenerator.init(56);
SecretKey secretKey = keyGenerator.generateKey();
byte[] encodedKey = secretKey.getEncoded();
return encodedKey;
}
/**
* DES加密
* @param encodedKey generateDESKey生成的密钥
* @param dataBytes byte[]形式的待加密数据
* @return byte[]
* @throws Exception
*/
public static byte[] encryptByDES(byte[] encodedKey, byte[] dataBytes) throws Exception {
SecretKey secretKey = new SecretKeySpec(encodedKey, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedData = cipher.doFinal(dataBytes);
return encryptedData;
}
/**
* DES解密
* @param encodedKey generateDESKey生成的密钥
* @param encryptedData byte[]形式的待解密数据
* @return byte[]
* @throws Exception
*/
public static byte[] decryptByDES(byte[] encodedKey, byte[] encryptedData) throws Exception {
SecretKey secretKey = new SecretKeySpec(encodedKey, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
return decryptedData;
}
基础版本使用方法如下:
@Test
public void testDES_1() throws Exception {
byte[] encodedKey = SecurityUtil.generateDESKey();
String data = "this is a good boy";
byte[] encryptedData = SecurityUtil.encryptByDES(encodedKey, data.getBytes());
byte[] decryptedData = SecurityUtil.decryptByDES(encodedKey, encryptedData);
Assert.assertEquals(data, new String(decryptedData));
}
可以看到,以上的方法使用起来并不友好,参数、返回等大量存在byte[],不便于理解,中间结果不便于查看和传输,比如如果需要将encryptedData返回给下游系统,那么还得使用Base64进行处理,基于此,我对在上述接口基础上进一步进行封装,使其使用起来更贴近日常使用场景。
优化版本:
/**
* 生成 DES 算法密钥
* @return 经过Base64编码的字符串密钥
* @throws Exception
*/
public static String generateDESKeyStr() throws Exception {
return Base64.encodeBase64String(generateDESKey());
}
/**
* DES加密
* @param key 经过Base64编码的字符串密钥
* @param data String形式的待加密数据
* @return 经过Base64编码的加密数据
* @throws Exception
*/
public static String encryptByDES(String key, String data) throws Exception {
byte[] encodedKey = Base64.decodeBase64(key);
byte[] dataBytes = data.getBytes();
byte[] encryptedData = encryptByDES(encodedKey, dataBytes);
return Base64.encodeBase64String(encryptedData);
}
/**
* DES解密
* @param key 经过Base64编码的字符串密钥
* @param data String形式的待解密数据
* @return 原始数据
* @throws Exception
*/
public static String decryptByDES(String key, String data) throws Exception {
byte[] encodedKey = Base64.decodeBase64(key);
byte[] dataBytes = Base64.decodeBase64(data);
byte[] decryptedData = decryptByDES(encodedKey, dataBytes);
return new String(decryptedData);
}
优化版本使用方法如下:
@Test
public void testDES_2() throws Exception {
String key = SecurityUtil.generateDESKeyStr();
String data = "this is a good boy";
String encryptedData = SecurityUtil.encryptByDES(key, data);
String decryptedData = SecurityUtil.decryptByDES(key, encryptedData);
Assert.assertEquals(data, decryptedData);
}
这里补充一下,在实际项目开发过程中,还真遇见不少同学对Base64理解有误的情况,对于以上处理和转换过程理解有难度的同学,可以戳一下这里
3DES
/**
* 生成 3DES 算法密钥
* @return byte[]
* @throws Exception
*/
public static byte[] generate3DESKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
// must be equal to 112 or 168
keyGenerator.init(168);
SecretKey secretKey = keyGenerator.generateKey();
byte[] encodedKey = secretKey.getEncoded();
return encodedKey;
}
/**
* 3DES加密
* @param encodedKey generate3DESKey生成的密钥
* @param dataBytes byte[]形式的待加密数据
* @return byte[]
* @throws Exception
*/
public static byte[] encryptBy3DES(byte[] encodedKey, byte[] dataBytes) throws Exception {
SecretKey secretKey = new SecretKeySpec(encodedKey, "DESede");
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedData = cipher.doFinal(dataBytes);
return encryptedData;
}
/**
* 3DES解密
* @param encodedKey generate3DESKey生成的密钥
* @param encryptedData byte[]形式的待解密数据
* @return byte[]
* @throws Exception
*/
public static byte[] decryptBy3DES(byte[] encodedKey, byte[] encryptedData) throws Exception {
SecretKey secretKey = new SecretKeySpec(encodedKey, "DESede");
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
return decryptedData;
}
使用方法如下:
@Test
public void test3DES() throws Exception {
byte[] encodedKey = SecurityUtil.generate3DESKey();
String data = "this is a good boy";
byte[] encryptedData = SecurityUtil.encryptBy3DES(encodedKey, data.getBytes());
byte[] decryptedData = SecurityUtil.decryptBy3DES(encodedKey, encryptedData);
Assert.assertEquals(data, new String(decryptedData));
}
AES
/**
* 生成 AES 算法密钥
* @return byte[]
* @throws Exception
*/
public static byte[] generateAESKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// must be equal to 128, 192 or 256
// 但是当你使用 192/256 时,会收到:
// java.security.InvalidKeyException: Illegal key size or default parameters
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
byte[] encodedKey = secretKey.getEncoded();
return encodedKey;
}
/**
* AES加密
* @param encodedKey generateAESKey生成的密钥
* @param dataBytes byte[]形式的待加密数据
* @return byte[]
* @throws Exception
*/
public static byte[] encryptByAES(byte[] encodedKey, byte[] dataBytes) throws Exception {
SecretKey secretKey = new SecretKeySpec(encodedKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedData = cipher.doFinal(dataBytes);
return encryptedData;
}
/**
* AES密
* @param encodedKey generateAESSKey生成的密钥
* @param encryptedData byte[]形式的待解密数据
* @return byte[]
* @throws Exception
*/
public static byte[] decryptByAES(byte[] encodedKey, byte[] encryptedData) throws Exception {
SecretKey secretKey = new SecretKeySpec(encodedKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
return decryptedData;
}
使用方法如下:
@Test
public void testAES() throws Exception {
byte[] encodedKey = SecurityUtil.generateAESKey();
String data = "this is a good boy";
byte[] encryptedData = SecurityUtil.encryptByAES(encodedKey, data.getBytes());
byte[] decryptedData = SecurityUtil.decryptByAES(encodedKey, encryptedData);
Assert.assertEquals(data, new String(decryptedData));
}
虽然AES支持128、192或 256的密钥长度,但是当我们使用192或256位长度的密钥时,会收到这个异常:java.security.InvalidKeyException: Illegal key size or default parameters
java.security.InvalidKeyException: Illegal key size or default parameters
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1026)
at javax.crypto.Cipher.implInit(Cipher.java:801)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at com.example.architecture.util.SecurityUtil.encryptByAES(SecurityUtil.java:161)
at com.example.architecture.util.SecurityUtilTest.testAES(SecurityUtilTest.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
原因是JRE中自带的local_policy.jar 和US_export_policy.jar是支持128位密钥的加密算法,而当我们要使用192或256位密钥算法的时候,已经超出它支持的范围。
解决方案:去官方下载JCE无限制权限策略文件。
JDK5 | JDK6 | JDK7| JDK8
下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
- 如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件。
- 如果安装了JDK,还要将两个jar文件也放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件。
AES128和AES256主要区别是密钥长度不同(分别是128bits,256bits)、加密处理轮数不同(分别是10轮,14轮),后者强度高于前者,当前AES是公认的较为安全的对称加密算法。
至此,上篇结束,更多精彩内容,欢迎继续阅读下篇。
参考资料:
https://baike.baidu.com/item/Blowfish/1677776
https://baike.baidu.com/item/对称加密/2152944
https://www.cnblogs.com/gordon0918/p/5317701.html
https://blog.csdn.net/u011414629/article/details/102645600
https://baike.baidu.com/item/非对称加密算法/1208652
https://www.jianshu.com/p/78886e480bef
https://blog.csdn.net/makenothing/article/details/88429511
https://blog.csdn.net/zlfing/article/details/77648444
https://blog.csdn.net/w47_csdn/article/details/87564029
猜你喜欢
- 2025-07-07 bitlocker 太恶心了(bitlocker manage)
- 2025-07-07 前后端安全机制(前后端安全机制有哪些)
- 2025-07-07 SpringBoot 接口加解密全过程详解
- 2025-07-07 加密算法的分类与应用(加密算法的种类)
- 2025-07-07 SpringBoot项目快速开发框架JeecgBoot——Web处理!
- 2025-07-07 网页端到端加密聊天系统(网页加密技术)
- 2024-10-10 简述无线网桥的主要加密方式 网桥怎么加密
- 2024-10-10 jsjiami.com.v7加密混淆文件隐藏关键代码的方法
- 2024-10-10 想不到一句”MD5加密“,竟然惨遭大量程序员的嘲笑!
- 2024-10-10 国内首个开源国密前后端分离快速开发平台,追求简洁至上大道至简
你 发表评论:
欢迎- 07-07使用AI开发招聘网站(100天AI编程实验)
- 07-07Tailwindcss 入门(tailwindcss中文文档)
- 07-07CSS 单位指南(css计量单位)
- 07-07CSS 定位详解(css定位属性的运用)
- 07-07程序员可以作为终身职业吗?什么情况下程序员会开始考虑转行?
- 07-07云和学员有话说:国企转行前端开发,斩获13K高薪!
- 07-0791年转行前端开发,是不是不该转,有啥风险?
- 07-07计算机图形学:变换矩阵(图形学 矩阵变换)
- 595℃几个Oracle空值处理函数 oracle处理null值的函数
- 587℃Oracle分析函数之Lag和Lead()使用
- 575℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 572℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 568℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 561℃【数据统计分析】详解Oracle分组函数之CUBE
- 548℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 541℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端懒加载 (49)
- 前端获取当前时间 (50)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)