1.RSA算法
简介:
1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。RSA加密算法是一种非对称加密算法。遵循私钥加密公钥解密、公钥加密私钥解密的模式。只有短的RSA钥匙才可能被强力方式解破。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。
原理:
简单介绍下素数和指数。
素数:素数又称质数,指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。
互质数:公因数只有1的两个数,叫做互质数。
模运算:模运算即求余运算。“模”是“Mod”的音译。和模运算紧密相关的一个概念是“同余”。数学上,当两个整数除以同一个正整数,若得相同余数,则二整数同余。
指数运算:指数运算又称乘方计算,计算结果称为幂。nm指将n自乘m次。把nm看作乘方的结果,叫做”n的m次幂”或”n的m次方”。其中,n称为“底数”,m称为“指数”。
RSA算法基于一个简单的数论事实,将两个大的素数想成十分容易,然而想要对其乘积进行因式分解则极其困难,因此可以将乘积公开作为加密秘钥。
秘钥的生成步骤:
<1>随机选择两个不相等的指数p和q
<2>计算出p和q的乘积N
<3>计算出p-1和q-1的乘积φN也叫欧拉函数
<4>随机选择整数e,e与m要互质并且0<e<φ(N)
<5>计算e的模反元素d
<6>公钥是(N,e)则私钥是(N,d)
加密解密过程:
<1>假设一个明文数m(0<m<N)
<2>将m加密成密文c,a.c=m^e mod N
<3>将密文c解密成m a.m=c^d mod N
用java封装RSA工具类
导入maven依赖:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>140</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
RSAUtil工具类:
public class RSAUtil {
//非对称密钥算法
public static final String KEY_ALGORITHM = "RSA";
/**
* 密钥长度,DH算法的默认密钥长度是1024
* 密钥长度必须是64的倍数,在512到65536位之间
*/
private static final int KEY_SIZE = 512;
//公钥
private static final String PUBLIC_KEY = "RSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 生成公私钥对
*
*/
public static Map<String, Object> initKey() throws Exception {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//甲方公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//甲方私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//将密钥存储在map中
Map<String, Object> keyMap = new HashMap<String, Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 私钥加密
*/
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);
}
/**
* 公钥加密
*
*/
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);
}
/**
* 私钥解密
*/
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);
}
/**
* 公钥解密
*/
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);
}
/**
* 取得私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
}
测试类:
public static void main(String[] args) throws Exception {
//初始化密钥
//生成密钥对
Map<String, Object> keyMap = RSAUtil.initKey();
//公钥
byte[] publicKey = RSAUtil.getPublicKey(keyMap);
//私钥
byte[] privateKey = RSAUtil.getPrivateKey(keyMap);
System.out.println("公钥:" + Base64.encodeBase64String(publicKey));
System.out.println("私钥:" + Base64.encodeBase64String(privateKey));
String str = "MIICeAIB";
System.out.println("原文:" + str);
//私钥加密
byte[] code1 = RSAUtil.encryptByPrivateKey(str.getBytes(), privateKey);
System.out.println("加密后的数据:" + Base64.encodeBase64String(code1));
String a=Base64.encodeBase64String(code1);
byte[] code3=Base64.decodeBase64(a);
//公钥解密
byte[] decode1 = RSAUtil.decryptByPublicKey(code3, publicKey);
System.out.println("解密后的数据:" + new String(decode1));
//公钥加密
byte[] code2 = RSAUtil.encryptByPublicKey(str.getBytes(), publicKey);
System.out.println("加密后的数据:" + Base64.encodeBase64String(code2));
String code4=Base64.encodeBase64String(code2);
//私钥解密
byte[] code5=Base64.decodeBase64(code4);
byte[] decode2 = RSAUtil.decryptByPrivateKey(code5, privateKey);
System.out.println("解密后的数据:" + new String(decode2));
}
2.数字签名
简介
数字签名(又称公钥数字签名、电子签章等)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
数字签名是非对称密钥加密技术与数字摘要技术的应用。
步骤
签名过程:
1.获得密钥对(RSA,DSA,ECDSA)
2.将需要签名消息进行hash计算得到消息摘要。(常见的消息摘要有MD5,SHA-256,SHA-512等)
3.将消息摘要进行非对称加密生成签名。
4.将原文和签名及公钥一起发送
验证过程:
1.接受者首先对原文进行hash计算
2.使用发送者公钥对签名进行解签
3.验证两者是否相同,若相同则证明未被篡
代码实例:
HashUtil:
package com.core.hash;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
public class HashUtil {
//md5消息摘要
public static String md5(byte[] data) throws NoSuchAlgorithmException{
MessageDigest md=MessageDigest.getInstance("MD5");
byte[] d= md.digest(data);
return byteToString(d);
}
//字节转化16进制表达式
public static String byteToString(byte[] data){
StringBuffer sb=new StringBuffer();
for (byte by:data) {
sb.append(Integer.toString((by & 0x0ff)+0x100,16).substring(1));
}
return sb.toString();
}
//sha1消息摘要
public static String sha1(byte[] data) throws NoSuchAlgorithmException{
MessageDigest md=MessageDigest.getInstance("SHA");
byte[] d= md.digest(data);
return byteToString(d);
}
//sha256消息摘要
public static String sha256(byte[] data) throws NoSuchAlgorithmException{
MessageDigest md=MessageDigest.getInstance("SHA-256");
byte[] d= md.digest(data);
return byteToString(d);
}
//sha512消息摘要
public static String sha512(byte[] data) throws NoSuchAlgorithmException{
MessageDigest md=MessageDigest.getInstance("SHA-512");
byte[] d= md.digest(data);
return byteToString(d);
}
//ripemd160消息摘要
public static String ripemd160(byte[] data) throws NoSuchAlgorithmException{
RIPEMD160Digest r=new RIPEMD160Digest();
r.update(data,0,data.length);
byte[] digest=new byte[r.getDigestSize()];
r.doFinal(digest, 0);
return byteToString(digest);
}
//将16进制字节表达式码转成字节数组,两两hash
public static byte[] hexStr2hexByte(String data){
if(null==data||0==data.length()){
return null;
}
data=(data.length()==1)?"0"+data:data;
byte[] arr=new byte[data.length()/2];
byte[] tmp=data.getBytes();
for (int i = 0; i < tmp.length/2; i++) {
arr[i]=unitByte(tmp[i*2], tmp[i*2+1]);
}
return arr;
}
public static byte unitByte(byte src0,byte src1){
byte b0=Byte.decode("0x"+new String(new byte[]{src0})).byteValue();
b0=(byte)(b0<<4);
byte b1=Byte.decode("0x"+new String(new byte[]{src1})).byteValue();
byte ret=(byte)(b0^b1);
return ret;
}
}
digitalSignature:
package com.core.test;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import com.core.RSA.RSAUtil;
import com.core.hash.HashUtil;
public class digitalSignature {
public static void main(String[] args) {
String str="数字签名的文件的完整性是很容易验证的(不需要骑缝章,骑缝签名,也不需要笔迹专家),"
+ "而且数字签名具有不可抵赖性(不可否认性)。"
+ "简单地说,所谓数字签名就是附加在数据单元上的一些数据,"
+ "或是对数据单元所作的密码变换。"
+ "这种数据或变换允许数据单元的接收者用以确认数据单元的来源和数据单元的完整性并保护数据,"
+ "防止被人(例如接收者)进行伪造。它是对电子形式的消息进行签名的一种方法,一个签名消息能在一个通信网络中传输。"
+ "基于公钥密码体制和私钥密码体制都可以获得数字签名,主要是基于公钥密码体制的数字签名。"
+ "包括普通数字签名和特殊数字签名。"
+ "普通数字签名算法有RSA、ElGamal、Fiat-Shamir、Guillou- Quisquarter、"
+ "Schnorr、Ong-Schnorr-Shamir数字签名算法、Des/DSA,椭圆曲线数字签名算法和有限自动机数字签名算法等。"
+ "特殊数字签名有盲签名、代理签名、群签名、不可否认签名、公平盲签名、门限签名、具有消息恢复功能的签名等,"
+ "它与具体应用环境密切相关。显然,数字签名的应用涉及到法律问题,"
+ "美国联邦政府基于有限域上的离散对数问题制定了自己的数字签名标准(DSS)。";
try {
/*
* 1.使用RSA算法获得公钥和公钥
*/
Map<String, Object> keyMap = RSAUtil.initKey();
byte[] publicKey = RSAUtil.getPublicKey(keyMap);
byte[] privateKey = RSAUtil.getPrivateKey(keyMap);
/*
* 2.发送方对字符串进行hash,获得消息摘要
*/
String hash=HashUtil.md5(str.getBytes());
System.out.println("---------------------------发送方开始-------------------------------");
System.out.println("文本消息摘要"+hash);
/*
* 3.使用私钥进行签名
*/
byte[] code1 = RSAUtil.encryptByPrivateKey(hash.getBytes(), privateKey);
System.out.println("签名为:" + Base64.encodeBase64String(code1));
String a=Base64.encodeBase64String(code1);
System.out.println("----------------------------------接收方验证------------------------------------");
/*
* 4.使用公钥解签
*/
System.out.println("接收收到的签名:" +a);
byte[] code3=Base64.decodeBase64(a);
byte[] decode1 = RSAUtil.decryptByPublicKey(code3, publicKey);
String b=new String(decode1);
System.out.println("接收方解签:" +b);
/*
* 5.接收方对字符串进行hash,获得消息摘要
*/
String hash1=HashUtil.md5(str.getBytes());
System.out.println("接收方原文摘要计算:"+hash1);
if(b.equals(hash1)){
System.out.println("消息正确是本人发送!");
}else{
System.out.println("消息错误非本人发送!");
}
System.out.println();
} catch (Exception e) {
e.printStackTrace();
}
}
}
RSAUtil往上翻可以找到。