最近项目用到一些加密和安全算法,浏览了一些资料,
总觉得缺少一篇让新手快速入门的基础而且比较全面的文章,
这里把一些常用的安全算法和自己的总结发出来,有不对的地方请大家多多指教,
Orz~-~
首先要了解用到的编码格式:
Base64编码,通过名字来看,也就是通过64个字符来表示信息的一种方法,并不要把它当做一种加密算法,因为任何人都可以通过Base64编码后的内容逆向得出编码之前的信息,这里可以简单的理解为通过Base64编码之后,将二进制信息编码成可打印的字符,便于在网络上传输,至于原理,想深入研究的,可以自行度娘。
用到的方法也就是 : 一个负责编码一个负责解码
Base64.encodeBase64String(byte[] b)
Base64.decodeBase64(String s)
这里引入 commons-codec-1.10.jar 这里只是其中的一种实现方式,原理都是相同的
十六进制编码:这里不需要再多说了,一般需要知道4位二进制数据对应一位16进制数据,十六进制是由0~9和A~F来表示,还有是不区分大小写的。
以上两种编码是比较常用的网络传输中使用到的编码。
接下来就列举出四种最常用的加密算法和实例:
第一种加密方法:消息摘要算法(摘要算法,哈希算法)
所谓的消息摘要算法,就是对一个消息或文本,产生一个固定长度的值(也就是摘要),采用的是单向的Hash函数对消息或文本进行计算产生,且不同的明文生成的摘要结果 "总是" 不同的,相同的明文产生的摘要 "必定" 一致。
这里目前最常用的摘要算法有 MD5 和 SHA1
MD5是由MD4,MD3,MD2改进而来,摘要长度为128位,在目前应用比较广泛。
主要功能代码如下:
MessageDigest md5=MessageDigest.getInstance("MD5");
BASE64Encoder base64 = new BASE64Encoder();
String newstr=base64.encode(md5.digest(str.getBytes("utf-8"))); //这里一般转为Base64或者16进制输出
SHA有SHA1,SHA-224,SHA-256,SHA-384等,目前应用最广泛的也就是SHA1和SHA256这两个,SHA1输出的摘要长度是160位,而SHA-256输出的也就是256位,
从输出位数上就可以比较出安全性,SHA1比SHA256要低,但相比于MD5的128位,SHA家族是更安全的,但是位数长带来的也有负面的影响,MD5速度要比SHA家族更快,
主要功能代码如下:
MessageDigest md5=MessageDigest.getInstance("SHA-1");
BASE64Encoder base64 = new BASE64Encoder();
String newstr=base64.encode(md5.digest(str.getBytes("utf-8"))); //这里一般转为Base64或者16进制输出
第二种加密方法:对称加密算法(应用比较少)
在对称加密算法中,数据发送方将原始数据和加密密钥一起经过特殊加密算法处理后,使其变成加密密文发送出去。
收信方收到密文后,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。
在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。
优点在于:算法公开,计算量小,加密速度快,加密效率高,加解密的告诉和使用长密钥时的难破解性
缺点:安全性过于依赖密钥,泄露密钥就意味着任何人都可以加解密数据,因此对秘钥的保存至关重要
目前流行的主要有DES和AES:DES的密钥长64位,但事实上只有56位参与运算(8和8的倍数都为效验位),相对于DES,AES算法作为新一代的数据加密标准,汇聚了强安全性,高性能,高效率,易用和灵活的优点,包含(128,192,256)三种秘钥长度,比DES算法的加密强度更高,更为安全。
DES和AES的实现过程类似,主要都是包括两部分,主要代码如下:
生成密钥:
public static String genKeyDES() throws Exception{
KeyGenerator keyGen = KeyGenerator.getInstance("DES"); //或者AES
keyGen.init(56); //128,192,256
SecretKey key = keyGen.generateKey();
BASE64Encoder base64 = new BASE64Encoder();
String base64Str = base64.encode(key.getEncoded());
return base64Str;
}
这里为了方便存储,一般将得到的DES密钥Base64编码成字符串,
将相应的密钥字符串转换为SecretKey对象,只需要将密钥Base64解码后,传入对应算法实例化SecretKeySpec即可
public static SecretKey loadKeyDES(String base64Key) throws Exception{
BASE64Decoder base64 = new BASE64Decoder();
byte[] bytes = base64.decodeBuffer(base64Key);
SecretKey key = new SecretKeySpec(bytes, "DES"); //或者AES
return key;
}
加密与解密:
//加密
public static byte[] encryptDES(byte[] suorce,SecretKey key) throws Exception{
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] bytes = cipher.doFinal(source);
return bytes;
}
public static final int ENCRYPT_MODE 用于将 Cipher 初始化为加密模式的常量
public static final int DECRYPT_MODE 用于将 Cipher 初始化为解密模式的常量
还有其他字段,这里就不列举了,
加解密都需要实例化Cipher对象
Cipher类位于javax.crypto包下 public class Cipher extends Object 此类为加密和解密提供密码功能。
为创建 Cipher 对象,应用程序调用 Cipher 的 getInstance方法并将所请求转换 的名称传递给它。还可以指定提供者的名称(可选)。
转换 是一个字符串,它描述为产生某种输出而在给定的输入上执行的操作(或一组操作)。
转换始终包括加密算法的名称(例如,DES,AES),后面可能跟有一个反馈模式和填充方案。
转换具有以下形式: “算法/模式/填充”或直接填写“算法”
第三种加密方法:非对称加密算法
非对称加密算法又称为公开密钥加密算法,需要两个密钥:一个称为公开密钥即为公钥,另一个称为私有密钥即为私钥,公钥和私钥配对使用,公钥加密只有对应私钥可以解密,私钥加密,只有对应公钥可以解密。
因为传输数据的两方分别保存的分别为公钥和私钥,所以被称作非对称加密。
这里加密和签名是由区别的:需根据具体情况选择
加密:既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密,私钥负责解密;
同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名,公钥负责验证;
优点:包含两种密钥,其中一个公开的,一个保密,即使在获取到公钥,加密算法的情况下,也无法获取公钥对应的私钥,也无法对密文解密。
缺点:由于其复杂程度高,加解密速度远远没有对称加密解密的速度快。
目前使用最广泛的非对称加密算法:RSA
主要代码实现如下:
1,初始化秘钥对:
/**
* 初始化密钥对
* @return Map 甲方密钥的Map
* */
public static Map<String,Object> initKey() throws Exception{
//实例化密钥生成器
KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance("RSA");
//初始化密钥生成器
keyPairGenerator.initialize(512); //密钥长度必须是64的倍数,在512到65536位之间
//生成密钥对
KeyPair keyPair=keyPairGenerator.generateKeyPair();
//甲方公钥
RSAPublicKey publicKey=(RSAPublicKey) keyPair.getPublic();
//甲方私钥
RSAPrivateKey privateKey=(RSAPrivateKey) keyPair.getPrivate();
}
2,公钥和私钥解密:
/**
* 私钥解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* */
public static byte[] decryptByPrivateKey(byte[] data,byte[] key) throws Exception{
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//数据解密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* */
public static byte[] decryptByPublicKey(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.DECRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
3:公钥和私钥加密:
/**
* 私钥加密
* @param data待加密数据
* @param key 密钥
* @return byte[] 加密数据
* */
public static byte[] encryptByPrivateKey(byte[] data,byte[] key) throws Exception{
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//数据加密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
* @param data待加密数据
* @param key 密钥
* @return byte[] 加密数据
* */
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);
}
4:通过Base64编码的字符串加载公钥和私钥
/**
* 得到公钥
*
* @param key
* 密钥字符串(经过base64编码)
* @throws Exception
*/
public static PublicKey getPublicKey(String base64PublicKey) throws Exception {
byte[] keyBytes = Base64.decode(base64PublicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
/**
* 得到私钥
*
* @param key
* 密钥字符串(经过base64编码)
* @throws Exception
*/
public static PrivateKey getPrivateKey(String base64PrivateKey) throws Exception {
byte[] keyBytes = Base64.decode(base64PrivateKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
第四种加密方法:数字签名
数字签名是对非对称加密技术和摘要算法的综合运用,也就是将通信内容的摘要信息使用发送方的私钥进行加密,然后将密文与原文一起传输,接收方使用公钥解密被加密的摘要,然后使用相同的摘要算法,对接收到的信息采用相同的方式产生摘要,与解密的摘要进行对比,相同则验证通过,说明信息是完整的。
发送方:这里发送方可以简单说先使用SHA1(其它摘要算法)对数据提取摘要,再用私钥加密,发送的数据为原始数据和加密后的数据
接收方:而接收方简单的可以理解为用对应的公钥解密摘要数据(这里可以验证信息是由对应私钥拥有者发出的,保证了数据发送者的身份),再通过发送的原始数据进行摘要算法,把两份数据进行比较(这里验证了信息的完整性,保证没有被篡改过)
常用的数字签名方法有:MD5withRSA,SHA1withRSA,
这里可以简单的理解为前面两种算法的结合,主要算法如下:
java都已经封装,
摘要和私钥加密生成签名
/**
* RSA签名
*
* @param content
* 待签名数据
* @param privateKey
* 私钥
* @param inputCharset
* 编码格式
* @return 签名值
*/
public static String genSign(String content, String privateKey, String inputCharset) {
try {
PrivateKey priKey = RSA.getPrivateKey(privateKey);
java.security.Signature signature = java.security.Signature.getInstance("SHA1withRSA");
signature.initSign(priKey);
signature.update(content.getBytes(inputCharset));
byte[] signed = signature.sign();
return Base64.encodeBase64String(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
摘要和公钥验证签名
/**
* RSA验签名检查
*
* @param content
* 待签名数据
* @param sign
* 签名值
* @param mchPublicKey
* 公钥
* @param inputCharset
* 编码格式
* @return 布尔值
*/
public static boolean verifySign(String content, String sign, String publicKey, String inputCharset) {
try {
PublicKey pubKey = RSA.getPublicKey(publicKey);
java.security.Signature signature = java.security.Signature.getInstance("SHA1withRSA");
signature.initVerify(pubKey);
signature.update(content.getBytes(inputCharset));
boolean bverify = signature.verify(Base64.decodeBase64(sign));
return bverify;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
以上只列举出了关键部分代码,都是测试过可以使用的,根据需要修改即可。