主页 > imtoken钱包app教程 > RSA算法原理及数字签名详解(附java实现源码)

RSA算法原理及数字签名详解(附java实现源码)

imtoken钱包app教程 2023-04-11 07:58:39

一、RSA算法介绍:

1977年,三位数学家Rivest、Shamir和Adleman设计了一种可以实现非对称加密的算法。 这个算法就是以他们三者的名字命名的,称为RSA算法。 RSA加密算法是一种非对称加密算法。 遵循私钥加密,公钥解密,公钥加密,私钥解密的模式。 只有短的 RSA 密钥才能被暴力破解。 只要密钥长度足够长,用RSA加密的信息实际上是无法被破解的。

原则:

素数和指数的简要介绍。

素数:素数,又称素数比特币的数字签名有哪两种技术组成,是指大于1的数,除1和整数本身外比特币的数字签名有哪两种技术组成,不能被其他自然数整除。

互质数:公因数仅为1的两个数称为互质数。

模运算:模运算就是取余运算。 “莫”是“莫”的音译。 与模运算密切相关的一个概念是“同余”。 在数学中,两个整数除以同一个正整数,如果得到相同的余数,则这两个整数是全等的。

指数运算:指数运算也称为幂计算,计算结果称为幂。 nm是指n乘以自己m次。 将 nm 视为幂的结果,称为“n 的 m 次方”或“n 的 m 次方”。 其中n称为“底数”,m称为“指数”。

RSA 算法基于一个简单的数论事实。 想出两个大素数很容易,但分解它们的积却极其困难。 因此,产品可以作为加密密钥公开。

密钥生成步骤:

随机选择两个不相等的索引 p 和 q

计算 p 和 q 的乘积 N

计算p-1和q-1的乘积φN,也叫欧拉函数

随机选择一个整数e,e和m必须互质且0

计算 e 的模逆元 d

公钥为(N, e),私钥为(N, d)

加解密过程:

假设一个明文数 m (0

将m加密成密文c,ac=m^e mod N

将密文c解密为m am=c^d mod N

用java封装RSA工具类

导入maven依赖:

 
            junit
            junit
            4.12
            test
      
	  
			  	bouncycastle
				bcprov-jdk16
				140
	  
      
                org.apache.commons
                commons-lang3
                3.3.2
      
      
			    commons-codec
			    commons-codec
			    1.11
	  

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 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 keyMap = new HashMap();
	        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 keyMap) {
	        Key key = (Key) keyMap.get(PRIVATE_KEY);
	        return key.getEncoded();
	    }
	    /** 
	     * 取得公钥
	     */
	    public static byte[] getPublicKey(Map keyMap) throws Exception {
	        Key key = (Key) keyMap.get(PUBLIC_KEY);
	        return key.getEncoded();
	    }
	   
	}

测试类:

public static void main(String[] args) throws Exception {
    //初始化密钥
    //生成密钥对
    Map 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. 对需要签名的消息进行哈希计算,得到消息摘要。 (常见的消息摘要包括MD5、SHA-256、SHA-512等)

3. 非对称加密消息摘要生成签名。

4.发送原文连同签名和公钥

验证过程:

1.接收方先对原文进行哈希计算

2.使用发送者的公钥对签名进行解签

3.验证两者是否相同,如果相同则证明没有被篡改

代码示例:

哈希工具:

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;
}
}

电子签名:

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 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。