为什么我们需要加密算法?
一般在开发过程中,我们为了保证敏感数据的安全性,才会对操作传输的数据进行加密,从而提高整个系统的安全性。
比如,客户端和服务端的数据交互传输,服务端将从数据库查询出来的数据通过加密的方式传递给客户端,客户端也将用户提交的数据加密后提交给服务端,两边都通过对应的解密规则进行解密,这样在数据传输的过程中,一些别有用心的人试图通过Fiddler这样的工具进行数据抓包而试图获得一些隐私数据将变得极其艰难,这也就达到了我们对数据安全性提升目的。
周一的时候,我接到了一个需求,某领导想要通过系统A免密跳转到系统B,因为领导觉得再去系统B输入一次用户名、密码、验证码实在是太繁琐了,尤其是在他有大量的审批工作要做的情况下。
如果两个系统统一做的CAS单点登录的前期框架性工作,这件事可以说根本不叫事,但是这个系统A和系统B都是近十年的老系统,肯定是没有这样的条件的,于是如何将免密登录的安全性提高就成了这个需求的重中之重了。
整体思路
先抛开加密本身,我先说一下我对这个需求的整体考虑:
具体步骤是:
- 系统A发起免密登录的请求
- 系统B通过加密算法得到密钥(公钥和私钥--非对称加密)
- 系统B将加密后的公钥返回给系统A
- 系统A解密公钥后使用公钥对数据进行加密并提交给系统B
- 系统B拿到密文后用私钥进行解析
- 解析成功就执行免密登录,失败就放弃该次操作。
整体的架构思路大致就是如此,这样就算别有用心的人使用抓包工具对这两次API请求都进行截获,也很难获取到真实的数据信息了。
加密算法选型
用什么算法?
众所周知,加密方式有两个常用的大方向,一个是对称加密,一个是非对称加密。
对称加密:用相同的密钥对原文进行加密和解密,通信双方共用一个密钥。
- 加密过程:原文 + 密钥 => 密文
- 解密过程:密文 - 密钥 => 原文
非对称加密:有两个密钥,即公钥(Public Key)和私钥(Private Key),对数据进行加密和解密使用不同的密钥。使用公钥进行加密,使用私钥进行解密。
- 加密过程:原文 + 公钥 => 密文
- 解密过程:密文 - 私钥 => 原文
对称加密的缺点:
对称加密算法的缺点:无法确保密钥被安全传递。如果密钥被截获,则整个加密密文都是不安全的。
对称加密的特点:
采用非对称加密算法即使第三方在网络上截获到密文,但其无法获得接收方的私钥,也就无法对密文进行解密,作为接收方务必保证自己私钥的安全,所以非对称加密技术解决了密钥传输过程的安全性问题。
好了,看到这里,大方向肯定定下来了,非对称加密跑不了了,然后我们看看非对称加密有哪些加密算法呢?
非对称加密算法类型:
RSA、Elgamal、背包算法、Rabin、Diffie-Hellman、ECC(椭圆曲线加密算法)。
使用最广泛的是RSA算法,Elgamal是另一种常用的非对称加密算法,考虑到成本和学习曲线的问题,我这次选择了ElGamal加密算法,因为两点:
-
ElGamal算法不是双向加解密的,RSA是双向加解密的。
双向加解密:公钥、私钥都可以进行加密和解密(公钥加密需要私钥解密、私钥加密需要公钥解密)
ElGamal我在线查了半天也没找到解密工具,而RSA我查到了(当然,不确定其是否可用)......
正所谓树大招风,与其使用RSA这种最常见的非对称加密,我还是决定选择一些相对没那么热门的加密方式,这样一些常见的在线破解网站也不会提供简单的破解渠道,从而进一步增加数据的安全性,但是冷门,也带来了一些冷门固有的问题,这个后面再说。
ElGamal算法在使用中的一些问题
1.Illegal key size or default parameters
当你找到一些ElGamal或者RSA的既有算法的DEMO后,兴高采烈的Run起它的main方法后,你有很大概率会遇到这个问题,一脸懵逼的你不用慌张,这是由于美帝的出口限制导致的加密算法Key长度的限制,具体原因:
每个国家,尤其是美国,对涉及密码的软件产品控制非常严格,在美国国内,很多密码算法长度都作了限制,而且某些算法在某些国家没有申请专利,可以"滥"用,而在某些国家却做了明确限制,不准使用,如此前提下,Sun必须按照惯例行事。
套用中国一句老话:上有政策,下有对策。Oracle也单独的放出了关于解决这个问题的无政策限制文件(local_policy.jar和US_export_policy.jar),我们只要下载对应JDK版本的文件并覆盖到指定路径(%JDK_Home%\jre\lib\security)下即可。
2.JDK并不直接支持Elgamal算法
这就是冷门带来的问题了,你并不能直接在JDK中使用ElGamal的算法支持,所以必须要引入两个jar包:
-
bouncycastle 下载地址:
-
commons-codec 下载地址:
这两个Jar包一个是对ElGamal算法本身提供支持的,另一个是对Base64提供支持的,都需要引入到项目中去。
3.ElGamal KeyFactory not available
在实际使用ElGamal算法的时候,我们不太可能只在一端使用公钥和私钥,这就面临着我们需要在另一个平台使用公钥加密的算法,常见的公钥加密算法是这样写的:
public static byte[] encryptByPublicKey(byte[] data,byte[] key) throws Exception{
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//数据加密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
当你在同一个程序中这样写公钥加密算法是没有问题的,因为你在启动main方法的时候,已经做好了initKey的相关工作了,但是你在另一个平台直接调用该方法则会报出错误:
java.security.NoSuchAlgorithmException: ElGamal KeyFactory not available
其实这也是冷门后遗症,因为JDK并没有实现ElGamal算法,所以不做初始化就直接用KeyFactory调用对应算法的KeyFactory实例,是肯定会报错的,解决方法就是自己做好初始化,走到天下都不怕:
public static byte[] encryptByPublicKey(byte[] data,byte[] key) throws Exception{
//加入对BouncyCastle支持
Security.addProvider(new BouncyCastleProvider());
AlgorithmParameterGenerator apg=AlgorithmParameterGenerator.getInstance(KEY_ALGORITHM);
//初始化参数生成器
apg.init(KEY_SIZE);
// 实例化密钥生成器
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//数据加密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
4.Cannot find any provider supporting ElGamal
这个问题就不是每个人都会遇到了,由于我是给服务端的同事写方法让他们各自调用自己那端需要的加密方法,所以我考虑了将他们需要的类封装成jar包的想法,于是我就将源码各自分离后,进行了基础的封装,然后通过Build FatJar将数据封装成jar包调用,测试调用的项目也顺利的引入了,一切就绪,运行main方法,然后就报错了.....
java.security.NoSuchAlgorithmException: Cannot find any provider supporting ElGamal
不支持Elgaml???明明已经加上了对应的初始化方法了啊,怎会不执行呢?
其实这里,不是不执行初始化的方法,而是所需的第三方jar在jar包内部并没有被正常加载导致的,我检查了一下jar的MANIFEST.MF文件发现合并打包时第三方丢失了Export-Package和Include-Resource对于第三方jar包的描述,解决方案可以查看:
https://www.cnblogs.com/skyme/articles/2316457.html
而我这个项目由于只需要两个jar包,所以我的解决方案是单独导出我的jar包,然后在新项目中独立引入我导出的jar包和bouncycastle包以及codec包,这样一切都可以正常执行了。
总结
以上就是我使用ElGamal算法时遇到的一些问题,希望大家在使用的过程中尽量避开这些问题,也为自己做个记录。