原创地址:https://mp.csdn.net/editor/html/115329236,欢迎转载。
目前java采用的主流加密方式主要有以下几种:
Base64加密
MD5摘要算法
SHA1、SHA256摘要算法
DES、3Des、AES、RC4、Rabbit、等对称加密解密
RSA、RSA2公钥私钥加密
需要具体实现的用法,可以跳过本文,点击相应的目录跳转到每种加密方式的具体实现。
Base64算法并不是加密算法,属于编码格式,是一种基于64个可打印字符来表示二进制数据的方法。作为网络上最常见的用于传输Bit字节代码的编码方式之一。Base64用于在HTTP环境下传递较长的标识信息。由于Base64的可逆性,通常我们在项目中将报文进行压缩、加密后,最后一步必然是使用base64编码,因为base64编码的字符串,更适合不同平台、不同语言的传输。我们一般不单独用Base64作为加密解密方法。
MD5全称是Message-Digest Algoorithm 5(信息-摘要算法),也是目前最主流的算法。
首先得明确的是,MD5是生成了信息摘要,为了确保信息完整性的一种摘要算法。MD5的典型应用是对一段Message(字节串)产生fingerprint(指纹),以防止被“篡改”。举个例子,系统为了确保你就是你,给你生成了一枚指纹,每次通过指纹识别,能判断你的身份。通过这种特性,我们常常使用的用法有以下几种:
当我们在某个资源下载网站,下载一份资料的时候,怎么判断你下载的资源就是网站提供的呢?这个时候就利用MD5,做一次MD5校验,以确保我们获得的文件与该站点提供的数字签名(Message-Digest)
文件为同一文件。举个例子,你将一段消息写在一个叫 content.txt文件中,对content.txt做一次MD5校验,产生一个MD5的值并记录,然后你可以传播这个文件给别人,别人如果修改了文件中的任何内容,你对这个文件重新计算MD5时就会发现(两个MD5值不相同)。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,或者防止文件被篡改,这就是所谓的数字签名应用。
如在UNIX系统中用户的密码是以MD5(或其它类似的算法)经Hash运算后存储在文件系统中。当用户登录的时候,系统把用户输入的密码进行MD5 Hash运算,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这可以避免用户的密码被具有系统管理员权限的用户知道。MD5将任意长度的“字节串”映射为一个128bit的大整数,并且是通过该128bit反推原始字符串是困难的,换句话说就是,即使你看到源程序和算法描述,也无法将一个MD5的值变换回原始的字符串,从数学原理上说,是因为原始的字符串有无穷多个,这有点象不存在反函数的数学函数。所以,要遇到了md5密码的问题,比较好的办法是:你可以用这个系统中的md5()函数重新设一个密码,如admin,把生成的一串密码的Hash值覆盖原来的Hash值就行了。
如何破解MD5呢?
尽管现在各种MD5破解工具层出不穷,但是MD5依然是常用,并且十分有效的加密手段。MD5还没有办法被逆向计算,最怕的就是“碰撞”。通俗讲,就是我在本地将“123456”,做一次MD5运算,生成了16位的信息摘要“49BA59ABBE56E057”,或者32位的“E10ADC3949BA59ABBE56E057F20F883E”,有一天我破解了数据库,得到了你的MD5运算密码,恰好是“E10ADC3949BA59ABBE56E057F20F883E”,那就碰撞上了,得知你的明文为“123456”。MD5碰撞的方法有很多,主要包括暴力枚举法、字典法、彩虹表法等等。
暴力枚举法顾名思义,就是简单粗暴地枚举出所有原文,并计算出它们的哈希值,看看哪个哈希值和给定的信息摘要一致。这种方法虽然简单,但是时间复杂度极高。想象一下,仅仅长度8位的密码就有多少种排列组合的可能性?
如果说暴力枚举法是ongoing时间换空间,那么字典法则是用空间换时间。黑客利用一个巨大的字典,存储尽可能多的原文和对应的哈希值。每次用给定的信息摘要查找字典,即可快速找到碰撞的结果。
彩虹表法较为麻烦,首先,假设要破解的密文位于某一链条的k-1位置处,对其进行Rk运算,看是否能够在末节点中找到对应的值。如果找到,则可以如前所述,使用起节点验证其正确性。否则,继续假设密文位于k-2位置处,这时就需要进行Rk-1、H、Rk两步运算,然后在末节点中查找结果。如是反复,最不利条件下需要将密文进行完整的R1、H、…Rk运算后,才能得知密文是否存在于彩虹表之中。
怎么保证不被碰撞破解呢?
加入盐值(salt)即可,只要明文相同,那么生成的MD5码就相同,于是攻击者就可以通过撞库的方式来破解出明文。加盐就是向明文中加入随机数,然后在生成MD5,这样一来即使明文相同,但由于随机数是不同(极少相同),所以每次生成的MD5码也不同,如此一来就大大增加了暴力破解的难度,使其几乎不可能破解。
SHA1全称是安全哈希算法(Secure Hash Algorithm)
主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。近些年来随着算力的增强,SHA1被证实存在弱点,许多组织建议用SHA-2或SHA-3来替换SHA-1。SHA-1是一种数据加密算法,该算法的思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。
同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
DES( Data Encryption Standard ) 是一种数据加密标准。
通常用KeyGenerator初始化此密钥生成器,使其具有确定的密钥大小,然后实例化DES密钥规则,利用Cipher类加密。
3DES,即3重DES,是DES的一个分支,密钥长度更长;但由于安全性问题;且违反柯克霍夫原则,使用频率低。
AES,可以使用128、192、和256位密钥,并且用128位分组加密和解密数据。近些年DES使用越来越少,原因就在于其使用56位密钥,比较容易被破解,逐渐被AES替代。相较于DES, AES的特点是一个SK扩展成多个子SK,轮加密。
Rabbit流密码(Rabbit Stream Cipher),密钥长度128位,最大加密消息长度为264 Bytes,即16 TB,若消息超过该长度,则需要更换密钥对剩下的消息进行处理。目前没有找到更详细的资料。
RSA是被研究得最广泛的公钥算法,从提出到现在已近三十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。RSA公开密钥密码体制的原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。明文和密文均是0至n-1之间的证书,n的大小通常为1024位的2进制,或309位的十进制。使用RSA一般需要产生公钥和私钥,当采用公钥加密时,使用私钥解密;采用私钥加密时,使用公钥解密。
RSA2是采用SHA256WithRSA作为标准签名算法的,RSA采用SHA1WithRSA,强制要求 RSA 密钥的长度至少为 2048。
如果我们仅需要一套便捷可用的加密工具,利用明文加时间戳的盐值进行MD5校验已经有足够抵御破解的能力。但是考虑到,一些涉及到资金安全的项目,或者与支付宝、京东打交道,那么我们就需要一套足够安全的加密机制来保证我们通信环境的安全。
如何利用RSA2+SHA256+BASE64来实现一套可靠的Java加密机制呢?
一、选择公私钥标准
首先,使用RSA一般需要产生公钥和私钥,为了保证公钥和私钥的隔离,我们一般采用X.509作为公钥证书格式标准,将PKCS8作为私钥格式标准。
X.509证书中主要含有公钥
、身份信息
、签名信息
和有效性信息
等信息。这些信息用于构建一个验证公钥的体系,用来保证客户端得到的公钥正是它期望的公钥。
公钥
: 非对称密码中的公钥。公钥证书的目的就是为了在互联网上分发公钥。
身份信息
: 公钥对应的私钥持有者的信息,域名以及用途等。
签名信息
: 对公钥进行签名的信息,提供公钥的验证链。可以是CA的签名或者是自签名,不同之处在于CA证书的根证书大都内置于操作系统或者浏览器中,而自签名证书的公钥验证链则需要自己维护(手动导入到操作系统中或者再验证流程中单独提供自签名的根证书)。
有效性信息
:证书的有效时间区间,以及# CRL等相关信息。
PKCS8是处理所有算法(不仅是RSA)的私钥的标准。PKCS8私钥结构包含了四个部分:版本号、算法标识、私钥数据、其他属性数据。
#PrivateKeyInfo语法在PKCS#8标准中定义如下:#
PrivateKeyInfo ::= SEQUENCE {
version Version,
privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
privateKey PrivateKey,
attributes [0] IMPLICIT Attributes OPTIONAL }
Version ::= INTEGER
PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
PrivateKey ::= OCTET STRING
Attributes ::= SET OF Attribute
二、生成公私钥,并用BASE64编码格式
利用java.security.KeyPairGenerator类生成一个初始化的密钥对,然后利用Base64.encodeBase64URLSafeString方法将公私钥的字符串进行编码,很多初学者会以为这是BASE64是一种安全的加密方式,实际上BASE64是字符串编码格式,urlsafe只是为了在URL上不出现标准BASE64的+/,而使用-_代替,因为URL编码会将+/编码为%xx的样式,从而能正确的识别公私钥格式。
// 生成密匙对
KeyPair keyPair = kpg.generateKeyPair();
// 得到公钥
Key publicKey = keyPair.getPublic();
String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
// 得到私钥
Key privateKey = keyPair.getPrivate();
String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
三、请求参数按照ASCII 码升序排序,并用&连接字符串
金融行业的接口,一次请求,少则十几个参数,多则几十个参数。这些参数通常要进行拼接后,进行加盐加密,然后和服务器端进行匹配。如果说各自按各自的习惯排序,那么拼接的明文肯定千差万别,尽管双方都是用相同的加盐加密方式,最后结果都不会相同,无法对接。
四、文本加签
新建KeyFactory密钥工厂,用于将密钥转换成RSA密钥规范,初始一个 Signature 对象 , 并用私钥对信息SHA256WithRSA签名,利用BASE64编码。
五、验签
将公钥进行BASE64解码,转化为X509公钥标准格式后用Signature的verify方法验签
六、加密操作:
X509 公钥加密(对应 PKCS8 私钥进行解密) 或者 PKCS8 私钥加密(对应X509公钥解密)
七、解密操作:
PKCS8 私钥解密(对应 X509 公钥加密)或者 X509 公钥解密(对应 PKCS8 私钥进行加密)