常用的加密算法

时间:2021-10-13 07:43:07

一。摘要算法

    1》MD5算法(Message Digest Algorithm 5) 可以保证数据传输完整性和一致性 摘要后长度为16字节 摘要信息中不包含原文信息

所有加密结果不可逆(无法解密) 一般在传送文件时 对源文件进行md5 hash 传送到对方后 检测hash值是否相等 如果相等文件传输正确

如果不相等 说明文件被篡改(加入木马)或者未传送完成

    其他MD算法 MD2(16字节)

     

public static void main(String[] args) throws NoSuchAlgorithmException {
MessageDigest md=MessageDigest.getInstance("MD5") ;
String code="hello";
byte[] bt=md.digest(code.getBytes());
System.out.println(bt.length);
}
    2》SHA算法Secure Hash Algorithm(安全hash算法) 安全散列算法(hash函数 将原始信息压缩 返回散列值)可以是SHA-1,SHA1是目前最安全

的摘要算法 摘要的长度为 20字节 

     其他的SHA 包括 SHA-256(32字节)

    

public static void main(String[] args) throws NoSuchAlgorithmException {
MessageDigest md=MessageDigest.getInstance("SHA") ;//或者SHA-1 SHA1
String code="hello";
byte[] bt=md.digest(code.getBytes());
System.out.println(bt.length);

}

二。编码和解码

   1》16进制 编码  计算机系统使用 2进制 为了编写存储方便一般将2进制 转换为16进制字符串 其中base64也是其中类似转换一种 16进制编码和base64都是

可逆的 一般用于存储

public static byte[] toByte(String src){
ByteArrayOutputStream baos=new ByteArrayOutputStream();
for(int i=0;i<src.length();i=i+2){
char fchar=src.charAt(i);
char nchar=src.charAt(i+1);
byte srcb=0;
if(fchar=='0'){
srcb=Byte.parseByte(nchar+"", 16);
}else{
srcb=(byte)(Integer.parseInt(fchar+""+nchar, 16));
}
baos.write(srcb);
}
return baos.toByteArray();
}


public static String toHex(byte[] src){
StringBuffer sb=new StringBuffer();
for(byte s:src){
//0XFF表示 8位的 11111111 和它&后 只剩下 8位 其他位都为0
String result=Integer.toHexString(s&0xFF);
if(result.length()==1){
result='0'+result;
}
sb.append(result);
}
return sb.toString();
}

  

 2》Base64编码  用于将字节数组和字符串互相转换

public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
byte[] src="hello".getBytes();
//摘要出来的结果为字节数组 存储到数据库不方便
MessageDigest md=MessageDigest.getInstance("SHA") ;
byte[] bt=md.digest(src);
//使用base64转换为字符串方便存储
BASE64Encoder base=new BASE64Encoder();
String str=base.encode(bt);
System.out.println(str);

//还原成字节数组
BASE64Decoder de=new BASE64Decoder();
byte[] bts=de.decodeBuffer(str);
System.out.println(bt.length==bts.length);
}

三。对称加密

   1》DES算法 (Data Encryptin Standard) 是对称加密算法的一种 使用秘钥加解密  秘钥必须是56字节  

      概念解释:

            秘钥 :用于加密和解密的钥匙  秘钥可以使用 getEncoded方法 获取byte[] 存储在文件系统中

            公钥和私钥:用于非对称加密的钥匙 公钥加密 私钥解密  私钥一般用于解密所以私钥一般存储在密钥库中

            口令:一般是自定义的字符串  可以通过口令和盐生成秘钥 

/**
* 生成56字节的秘钥
*/
public static SecretKey genKey(int len) throws NoSuchAlgorithmException{
KeyGenerator kg=KeyGenerator.getInstance("DES");
kg.init(len);
return kg.generateKey();
}
public static void main(String[] args) throws Exception {
//SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES");
SecretKey sk=genKey(57);
//---------加密
String password="tiger";
Cipher cipher=Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, sk);
//被加密之后获取的字节数组
byte[] mcontent=cipher.doFinal(password.getBytes());
//---------解密
Cipher cipher1=Cipher.getInstance("DES");
cipher1.init(Cipher.DECRYPT_MODE, sk);
System.out.println(new String(cipher1.doFinal(mcontent)));


}

   2》AES算法 (Advanced Encryptin Standard 高级加密标准) 是对称加密算法一种升级 因为 56位秘钥 在计算机系统性能越来越高的前提下 56位很容易被

  破解 所以 AES将秘钥的长度提高到128, 192 or 256  必须是这三个数 128默认可以使用  192和256由于美国限制 需要相关授权 否则抛出异常

public static final String AL="AES";
/**
* 生成56字节的秘钥
*/
public static SecretKey genKey(int len) throws NoSuchAlgorithmException{
KeyGenerator kg=KeyGenerator.getInstance(AL);
kg.init(len);
return kg.generateKey();
}
public static void main(String[] args) throws Exception {
//SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES");
SecretKey sk=genKey(128);
//---------加密
String password="tiger";
Cipher cipher=Cipher.getInstance(AL);
cipher.init(Cipher.ENCRYPT_MODE, sk);
//被加密之后获取的字节数组
byte[] mcontent=cipher.doFinal(password.getBytes());
//---------解密
Cipher cipher1=Cipher.getInstance(AL);
cipher1.init(Cipher.DECRYPT_MODE, sk);
System.out.println(new String(cipher1.doFinal(mcontent)));


}

 3》PBE算法(Password Base Encryption) 基于自定义口令的加解密算法  定义口令 同时还必须定义 盐和 使用盐混淆的次数 

加解密过程中 该三个参数都必须一致

//盐 用于将明文进行多次混淆
static byte[] salt = new byte[8];
static Random r = new Random();
static int saltCount=100;
static{
r.nextBytes(salt);
}
public static final String AL="PBEWithMD5AndDES";
/**
* 生成自定义口令的秘钥
*/
public static SecretKey genKey(String kl) throws Exception{
char[] klChar=kl.toCharArray();
PBEKeySpec pbe=new PBEKeySpec(klChar);
SecretKeyFactory skf=SecretKeyFactory.getInstance(AL);
return skf.generateSecret(pbe);
}
/**
* 使用口令和盐进行加密
*/
public static byte[] encrypt(SecretKey key,byte[] src) throws Exception{
Cipher cipher=Cipher.getInstance(AL);
//使用口令 盐(100次混淆)
PBEParameterSpec parameter=new PBEParameterSpec(salt, saltCount);
cipher.init(Cipher.ENCRYPT_MODE, key,parameter);
//被加密之后获取的字节数组
byte[] mcontent=cipher.doFinal(src);
return mcontent;
}
/**
* 使用口令和盐进行解密 盐和口令和混淆的次数都必须和加密之前一致
*/
public static byte[] decrypt(SecretKey key,byte[] src) throws Exception{
Cipher cipher=Cipher.getInstance(AL);
//使用口令 盐(100次混淆)
PBEParameterSpec parameter=new PBEParameterSpec(salt, saltCount);
cipher.init(Cipher.DECRYPT_MODE, key,parameter);
//被加密之后获取的字节数组
byte[] mcontent=cipher.doFinal(src);
return mcontent;
}


public static void main(String[] args) throws Exception {
//SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES");
SecretKey sk=genKey("123456");
//---------加密
String password="tiger";
byte[] mw=encrypt(sk, password.getBytes());
//---------解密
System.out.println(new String(decrypt(sk, mw)));
}


四。非对称加密

  1》DH算法  是一种对称加密到非对称加密的过度算法 使用DH算法生成密钥对 使用对称加密算法获取秘钥后 进行加解密 双方必须都存在公钥和私钥

public static void main(String[] args) throws Exception {
// 生成DH密钥对
KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH");
kpg.initialize(512);
KeyPair kp=kpg.generateKeyPair();
PublicKey pk=kp.getPublic();
PrivateKey prk=kp.getPrivate();
//实例化
KeyAgreement keyAgree = KeyAgreement.getInstance("DH");
//初始化
keyAgree.init(prk);
keyAgree.doPhase(pk, true);
//生成本地密钥
SecretKey secretKey = keyAgree.generateSecret("DES");

//还是使用对称方式加解密
//使用本地秘钥加密
Cipher cip=Cipher.getInstance("DES");
cip.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] mw=cip.doFinal("test".getBytes());
//使用本地秘钥解密
Cipher cip1=Cipher.getInstance("DES");
cip1.init(Cipher.DECRYPT_MODE, secretKey);
System.out.println(new String(cip1.doFinal(mw)));

}
2》RSA算法  目前影响力最大的非对称加密算法  一般公钥对外公开 加密后传送给服务器 服务器使用独有的私钥解密(当然也可以私钥加密 公钥解密 一般不这样 因为谁都有公钥都能解密 加密就没有意义了) 加密的数据在传输过程是  无法破解的  秘钥对初始化大小必须是64的倍数  实际值 只能在512-1024中

public static void main(String[] args) throws Exception {
// 生成RSA密钥对
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp=kpg.generateKeyPair();
PublicKey pk=kp.getPublic();
PrivateKey prk=kp.getPrivate();

//公钥加密
Cipher cip=Cipher.getInstance("RSA");
cip.init(Cipher.ENCRYPT_MODE, pk);
byte[] mw=cip.doFinal("test".getBytes());
//私钥解密
Cipher cip1=Cipher.getInstance("RSA");
cip1.init(Cipher.DECRYPT_MODE, prk);
System.out.println(new String(cip1.doFinal(mw)));

}

RSA算法中 一般公钥和私钥都都可以调用getEncoded()转换为byte数组 使用base64编码后 存储在文件中 方便公钥分发 读取文件后需要转换对应

的公钥和私钥的方法为:

/**
* 读取公钥字节数组转换为对象
* @throws Exception
*/
public PublicKey getPub(byte[] bt) throws Exception{
X509EncodedKeySpec x=new X509EncodedKeySpec(bt);
KeyFactory fac=KeyFactory.getInstance("RSA");
return fac.generatePublic(x);
}
/**
* 读取私钥字节数组转换为对象
* @throws Exception
*/
public PrivateKey getPri(byte[] bt) throws Exception{
PKCS8EncodedKeySpec x=new PKCS8EncodedKeySpec(bt);
KeyFactory fac=KeyFactory.getInstance("RSA");
return fac.generatePrivate(x);
}


五。数据签名

   签名是非对称加密技术和摘要技术的综合运用用户A将明文和使用私钥加密的明文摘要一起发送给用户B 用户B使用公钥解密出摘要 然后使用相同的摘要

算法将明文摘要  将两个摘要字符串比较如果相等 则表明内容没有被篡改

  原理实现过程如下

public static void main(String[] args) throws Exception {
//获取公钥和私钥
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp=kpg.generateKeyPair();
PublicKey pk=kp.getPublic();
PrivateKey prk=kp.getPrivate();
//使用私钥签名
String message="hello my name is jiaozi";
//返回的byte就可以进行传输
byte[] srcByte=sign(message.getBytes(),prk);
//假设这里模拟篡改数据 肯定会出现异常 或者检验不通过
srcByte[9]=10;
//存在公钥的用户 接受到该srcByte 就可以验证是否被篡改
System.out.println(verify(srcByte,pk));
}
/**
* 签名过程
*/
public static byte[] sign(byte[] content,PrivateKey pk) throws Exception{
//对明文进行摘要
MessageDigest md=MessageDigest.getInstance("MD5");
byte[] zy=md.digest(content);

//对摘要进行加密
Cipher cp=Cipher.getInstance("RSA");
cp.init(Cipher.ENCRYPT_MODE, pk);
byte[] enZy=cp.doFinal(zy);
//要一起传送的数据 双方约定好使用Map
Map map=new HashMap();
map.put("content", content);
map.put("enZy", enZy);
//传输过程使用byte数组 这里使用序列化将对象打包转换为字节数组
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(map);
oos.close();
return baos.toByteArray();
}

/**
* 验证签名过程
*/
public static boolean verify(byte[] content,PublicKey pk) throws Exception{
//将获取的数据转换为Map
ByteArrayInputStream baos=new ByteArrayInputStream(content);
ObjectInputStream oos=new ObjectInputStream(baos);
Map map=(Map)oos.readObject();
oos.close();
//获取到明文和加密的摘要信息
byte[] srcContent=(byte[])map.get("content");
byte[] enZy=(byte[])map.get("enZy");

//使用相同的摘要算法 将明文摘要
MessageDigest md=MessageDigest.getInstance("MD5");
byte[] contentZy=md.digest(srcContent);
//将加密的摘要解密
Cipher cp=Cipher.getInstance("RSA");
cp.init(Cipher.DECRYPT_MODE, pk);
byte[] zy=cp.doFinal(enZy);
BASE64Encoder bas=new BASE64Encoder();
if(bas.encode(contentZy).equals(bas.encode(zy))){
return true;
}
return false;
}
自己实现这个过程略显复杂 java提供了MD5WithRSA和SHA1WithRAS算法直接实现上述过程

public static void main(String[] args) throws Exception {
//获取公钥和私钥
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp=kpg.generateKeyPair();
PublicKey pk=kp.getPublic();
PrivateKey prk=kp.getPrivate();
//使用私钥签名
String message="hello my name is jiaozi";
//返回的byte和明文就可以进行传输
byte[] hash=sign(message.getBytes(),prk);
//存在公钥的用户 接受到该srcByte 就可以验证是否被篡改
System.out.println(verify(message.getBytes(),hash,pk));


}
/**
* 签名过程 返回的是加密的摘要
*/
public static byte[] sign(byte[] content,PrivateKey pk) throws Exception{
Signature si=Signature.getInstance("MD5WithRSA");//或者使用SHA1WithRSA
si.initSign(pk);
si.update(content);
return si.sign();
}

/**
* 验证签名过程 content表示原文可能获取原文的hash hash表示sign函数返回的加密摘要
*/
public static boolean verify(byte[] content,byte[] hash,PublicKey pk) throws Exception{
Signature si=Signature.getInstance("MD5WithRSA");//或者使用SHA1WithRSA
si.initVerify(pk);
si.update(content);
return si.verify(hash);
}