1. 程式人生 > >RSA資料摘要+數字簽名(Java)

RSA資料摘要+數字簽名(Java)

背景

之前我一直不明白,比如A、B雙方加密通訊的時候,為什麼A要使用B的公鑰來加密,為A什麼不使用A的私鑰加密,然後B用A的公鑰解密不就可以了嗎?

其實,A的私鑰主要是A用來簽名的,簽名顧名思義是表明這個東西是A傳送的,而不是別人發過來的。為什麼A用自己的私鑰簽名就說明這個東西就是A發過來的呢?因為用B用A的公鑰解籤的時候,得到了原文的資料摘要,然後B根據得到的明文資訊然後使用Hash函式重新得到資料摘要,兩個資料摘要一對比,如果一致,那麼就是A發過來的,表明了即使A發過來的且原文資訊沒有被改動過。

具體流程圖下圖所示:

在這裡插入圖片描述


目標

使用Java語言,實現RSA資料摘要+資料簽名。


程式碼

程式入口檔案:MainEntrance

package RSA;



/**
 * @author yourname
 * @date 2018年10月16日 下午5:12:18
 * 
 */
public class MainEntrance {

	
	 public static void main(String[] args) {
	        
	        try {
				new Test();
				
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
}

視窗介面以及事件處理檔案:Test

package RSA;

import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Map;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

/**
 * @author yourname
 * @date 2018年10月16日 下午6:45:43
 * 
 */
public class Test extends JFrame {


    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	String a2="";
     int pd;
     Map<String, Object> keyMap;
     String  textPlainMD5 = "";
     String byte2Base64 = "";
     byte[] publicDecrypt = null;
     
    public Test() throws Exception
    {
    		//使用Map存放初始化的公鑰和私鑰
    		keyMap = CreateSecrteKey.initKey();
    	
    		//建立容器和控制元件
        Container c1 = getContentPane();
        getContentPane().setLayout(null);//佈局
        getContentPane().setBackground(Color.LIGHT_GRAY);
        JLabel jl1 =new JLabel("請輸入明文:");
        JLabel jl2 =new JLabel("選擇方式:"); 
        JLabel jl3 =new JLabel("結果:");  

        JTextField jt1 = new JTextField(10);
        
        JLabel jt3 = new JLabel("");
        //設定jt3的字型
        jt3.setFont(new Font("宋體",Font.BOLD, 8));


         JButton jb = new JButton("開始");
         jb.setBorderPainted(true);//設定按鈕顯示邊界
         
         JButton jb1 = new JButton("反解");
         jb1.setBorderPainted(true);//設定按鈕顯示邊界

         JRadioButton jr1 = new JRadioButton("提取資料摘要(MD5)");//單選按鈕
         JRadioButton jr2 = new JRadioButton("RSA資料簽名");
         ButtonGroup group = new ButtonGroup();//沒有這句,你的單選按鈕就不叫單選按鈕,可以自己去掉試試
         group.add(jr1);
         group.add(jr2);
         
    			//控制元件加入到容器
            c1.add(jl1);
            c1.add(jt1);
            c1.add(jl2);
            c1.add(jr1);
            c1.add(jr2);
            c1.add(jl3);
            c1.add(jt3);
            c1.add(jb);
            c1.add(jb1);
            setSize(400, 200);
            setVisible(true);
            setTitle("RSA簽名演算法");
            
            //設定各個控制元件的絕對位置
            jl1.setBounds(5,5,80,20);
            
            jt1.setBounds(85, 5, 150, 20);
            
            jl2.setBounds(5, 25,	80, 20);
            
            jr1.setBounds(85, 25, 160, 25);
            
            jr2.setBounds(85,65,140,25);
            
            jl3.setBounds(5,95,80,25);
            
            jt3.setBounds(85, 95, 200, 70);
            jt3.setOpaque(true);
            jt3.setBackground(Color.cyan);
            
            jb.setBounds(285, 95, 60, 30);
            
            jb1.setBounds(285,125,60,30);
            
            //"反解按鈕"監聽事件
            jb1.addActionListener(
            		new ActionListener() {
						
						@Override
						public void actionPerformed(ActionEvent e) {
							// TODO Auto-generated method stub
							//對資料摘要進行Hash對比
							if(pd==1)
							{
								if(textPlainMD5.length()>0)
								{
									String e1 = jt1.getText();//輸入的資料
									//使用MD5對原文資訊提取資料摘要
									String textPlainMD51 = CreateSecrteKey.MD5(e1);
									//把解簽得到的Hash值和原文重新MD5生成的Hash值對比
									if(new String(publicDecrypt).equals(textPlainMD51))
									{
										System.out.println("原文資訊的Hash值和解簽得到的Hash一樣的");
										try {
											JlabelSetText(jt3, "原文資訊的Hash值和解簽得到的Hash一樣的");
										} catch (InterruptedException e2) {
											// TODO Auto-generated catch block
											e2.printStackTrace();
										}
									}
								}
								else
								{
									JOptionPane.showMessageDialog(null, "請先完成資料摘要提取和資料簽名!", "提示", JOptionPane.ERROR_MESSAGE);
								}
							}
							//對資料簽名進行解籤
							else if(pd==2)
							{
								if(byte2Base64.length()>0)
								{
									try {
	                      	            //獲得公鑰
	                      	            String publicKey_Str = CreateSecrteKey.getPublicKey(keyMap);
//	                      	            System.out.println(publicKey_Str);
	                      	            //獲得公鑰PK物件
	                      	            PublicKey publicKey = CreateSecrteKey.string2PublicKey(publicKey_Str);
	                      	            //對密文進行Base64解碼
	                      	            byte[] base642Byte = CreateSecrteKey.base642Byte(byte2Base64);
	                      	            //用公鑰解籤
	                      	             publicDecrypt = CreateSecrteKey.publicDecrypt(base642Byte, publicKey);
	                      	            //解密後的資料摘要
//	                      				System.out.println("解密後的MD5: " + new String(publicDecrypt));
	                      	            JlabelSetText(jt3, new String(publicDecrypt));
									} catch (Exception e2) {
										// TODO: handle exception
									}
								}
								else
								{
									JOptionPane.showMessageDialog(null, "請先完成資料摘要提取和資料簽名!", "提示", JOptionPane.ERROR_MESSAGE);
								}
							}
						}
					}
            		);
            
            
          //"開始按鈕"轉換按鈕新增監聽事件
            jb.addActionListener(
                    new ActionListener(){

                        public void actionPerformed(ActionEvent arg0){
                        	//彈出對話方塊
                        String e1 = jt1.getText();//輸入的資料                         
//                        System.out.println("e1:"+e1);
                         //如果沒有輸入明文資訊,就提示錯誤    
                        	if(e1.equals(""))
                        	{
                        		JOptionPane.showMessageDialog(null, "請先輸入明文資訊!", "提示", JOptionPane.ERROR_MESSAGE);
                        	} 
                        	//選擇提取資料摘要的按鈕處理事件
                        	if(pd==1&&e1.length()>0)
                        	{
                        		//使用MD5對原文資訊提取資料摘要
                	            textPlainMD5 = CreateSecrteKey.MD5(e1);
                	            //顯示摘要資訊,並自動換行
                	            try {
    								JlabelSetText(jt3, textPlainMD5);
    							} catch (InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
                        	}
                        	//選擇RSA資料簽名的按鈕處理事件
                        	else if(pd==2&&e1.length()>0)
                        	{
                      	        try {
                      	            //獲得私鑰
                      	            String privateKey_Str = CreateSecrteKey.getPrivateKey(keyMap);
//                      	            System.out.println(privateKey_Str);
                      	            
                      	            //獲得私鑰PK物件
                      	            PrivateKey privateKey = CreateSecrteKey.string2PrivateKey(privateKey_Str);

                      	            //用私鑰簽名
                      	            byte[] privateEncrypt = CreateSecrteKey.privateEncrypt(textPlainMD5.getBytes(), privateKey);
                      	            //加密內容用Base64編碼(密文)
                      	             byte2Base64 = CreateSecrteKey.byte2Base64(privateEncrypt);
//                      	            System.out.println("資料簽名:"+byte2Base64);
                      	            
                      	            //顯示簽名信息,並自動換行
                      	            JlabelSetText(jt3, byte2Base64);
                      				
                      	        } catch (Exception e) {
                      	            e.printStackTrace();
                      	        }                  	    
                        	}
                        	}
                    });

        jr1.addActionListener(//提取資料摘要按鈕
                new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        // TODO 自動生成的方法存根
                    	//提取資料摘要時候標記為1
                        pd=1;
                    }
                }
                );
        jr2.addActionListener(//RSA資料簽名按鈕
                new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        // TODO 自動生成的方法存根
                    	//資料簽名的時候標記為2
                        pd=2;
                    }
                }
                );    
    }
    
    
    
    
    
    
    //讓JLabel自動換行
    void JlabelSetText(JLabel jLabel, String longString) 
            throws InterruptedException {
        StringBuilder builder = new StringBuilder("<html>");
        char[] chars = longString.toCharArray();
        FontMetrics fontMetrics = jLabel.getFontMetrics(jLabel.getFont());
        int start = 0;
        int len = 0;
        while (start + len < longString.length()) {
            while (true) {
                len++;
                if (start + len > longString.length())break;
                if (fontMetrics.charsWidth(chars, start, len) 
                        > jLabel.getWidth()) {
                    break;
                }
            }
            builder.append(chars, start, len-1).append("<br/>");
            start = start + len - 1;
            len = 0;
        }
        builder.append(chars, start, longString.length()-start);
        builder.append("</html>");
        jLabel.setText(builder.toString());
    }
    
    
}

工具類檔案(公私鑰生成、MD5、Base64等):CreateSecrteKey

package RSA;

import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;


/**
 * @author yourname
 * @date 2018年10月17日 上午12:26:27
 * 
 */
public class CreateSecrteKey {
public class Keys {
        
    }
	//使用RSA演算法
    public static final String KEY_ALGORITHM = "RSA";
    //公鑰
    private static final String PUBLIC_KEY = "RSAPublicKey";
    //私鑰
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    //獲得公鑰
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        //獲得map中的公鑰物件 轉為key物件
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        //byte[] publicKey = key.getEncoded();
        //編碼返回字串
        return encryptBASE64(key.getEncoded());
    }

    //獲得私鑰
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        //獲得map中的私鑰物件 轉為key物件
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        //byte[] privateKey = key.getEncoded();
        //編碼返回字串
        return encryptBASE64(key.getEncoded());
    }

    //解碼返回byte
    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    //編碼返回字串
    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }
    
    //map物件中存放公私鑰
    public static Map<String, Object> initKey() throws Exception {
        //獲得物件 KeyPairGenerator 引數 RSA 1024個位元組
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        //通過物件 KeyPairGenerator 獲取物件KeyPair
        KeyPair keyPair = keyPairGen.generateKeyPair();
        
        //通過物件 KeyPair 獲取RSA公私鑰物件RSAPublicKey RSAPrivateKey
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //公私鑰物件存入map中
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    //私鑰加密
  	public static byte[] privateEncrypt(byte[] content, PrivateKey privateKey) throws Exception{
  		Cipher cipher = Cipher.getInstance("RSA");
  		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  		byte[] bytes = cipher.doFinal(content);
  		return bytes;
  	}
  	
  	//公鑰解密
  	public static byte[] publicDecrypt(byte[] content, PublicKey publicKey) throws Exception{
  		Cipher cipher = Cipher.getInstance("RSA");
  		cipher.init(Cipher.DECRYPT_MODE, publicKey);
  		byte[] bytes = cipher.doFinal(content);
  		return bytes;
  	}
  	
  	//位元組陣列轉Base64編碼
  	public static String byte2Base64(byte[] bytes){
  		BASE64Encoder encoder = new BASE64Encoder();
  		return encoder.encode(bytes);
  	}
  	
  	//Base64編碼轉位元組陣列
  	public static byte[] base642Byte(String base64Key) throws IOException{
  		BASE64Decoder decoder = new BASE64Decoder();
  		return decoder.decodeBuffer(base64Key);
  	}

	//將Base64編碼後的公鑰轉換成PublicKey物件
	public static PublicKey string2PublicKey(String pubStr) throws Exception{
		byte[] keyBytes = base642Byte(pubStr);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PublicKey publicKey = keyFactory.generatePublic(keySpec);
		return publicKey;
	}
	
	//將Base64編碼後的私鑰轉換成PrivateKey物件
	public static PrivateKey string2PrivateKey(String priStr) throws Exception{
		byte[] keyBytes = base642Byte(priStr);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
		return privateKey;
	}

	public static String MD5(String s) {
	    try {
	        MessageDigest md = MessageDigest.getInstance("MD5");
	        byte[] bytes = md.digest(s.getBytes("utf-8"));
	        return toHex(bytes);
	    }
	    catch (Exception e) {
	        throw new RuntimeException(e);
	    }
	}

	public static String toHex(byte[] bytes) {

	    final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
	    StringBuilder ret = new StringBuilder(bytes.length * 2);
	    for (int i=0; i<bytes.length; i++) {
	        ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
	        ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
	    }
	    return ret.toString();
	}
}


程式演示

一、開始執行介面
(1) 執行程式的時候,就已經生成了公鑰和私鑰,每執行一次就會得到不同的公私鑰對,生成的公私鑰對方便下面使用。
在這裡插入圖片描述

二、輸入原文資訊,開始提取原文資訊的資料摘要
(1) 輸入框輸入原文資訊
(2) 點選"提取資料摘要(MD5)"的選項
(3) 點選"開始"按鈕,得到資料摘要
在這裡插入圖片描述

三、對資料摘要進行簽名
(1) 步驟二已經得到了資料摘要
(2) 點選"RSA資料簽名"選項,然後點選"開始按鈕",使用上面得到的私鑰對資料摘要進行簽名。
在這裡插入圖片描述

四、對資料簽名進行解籤
(1) 選擇"RSA資料簽名"選項,然後點選"反解"按鈕。反解過程就是使用上面得到的公鑰對資料簽名進行解籤,得到資料摘要。
在這裡插入圖片描述

五、驗證資料完整性
(1) 選擇"提取資料摘要(MD5)"選項,然後點選"反解"按鈕。這個步驟就是把原文資訊重新用Hash生成資料摘要Q1,然後用Q1跟步驟四中得到的資料摘要Q2對比,如果Q1和Q2一樣就輸入一樣的提示。如果不一致則提示原文資訊被修改。
在這裡插入圖片描述


可能遇到的問題

return (new BASE64Encoder()).encodeBuffer(key);

這句程式碼可能會提示錯誤,因為Base64Encoder()方法是JDK8之前的方法,使用JDK8或者以上的則不能使用這個方法。
如果要使用這個方法,需要對專案配置進行修改。

選擇你的專案–>JRE System Library–> Build Path–>Confugure Build Path

在這裡插入圖片描述

選擇Libraries–> Access rules–>Edit在這裡插入圖片描述

點選"Add"–>選擇"Accessible"–>Rule Rattern寫上"**",點選儲存
在這裡插入圖片描述

最近訪問

资讯网证券交易所周末能开户吗传统火锅店起名魔兽争霸修改器八月初五做seo怎么赚钱电子商务网站建设要多少钱烁的意思起名seo技术博客周易系列电视剧seo排名不稳定陕西seo优化给肚子里的宝宝起个乳名适合男生用的英文网名睢县是哪个省哪个市特朗普最新最新消息什么写作文青岛seo整站优化周易取名起名大全测分东莞餐饮网站建设电影最新上映合肥网站建设费用宝宝免费起名名字测试打分生辰八字周公解梦梦到丢东西温暖的时刻周公解梦开白花孟州网站建设myself是什么意思公司名典起名网seo职位是的社区推广营销方式少年生前被连续抽血16次?多部门介入两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”淀粉肠小王子日销售额涨超10倍高中生被打伤下体休学 邯郸通报单亲妈妈陷入热恋 14岁儿子报警何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言张家界的山上“长”满了韩国人?男孩8年未见母亲被告知被遗忘中国拥有亿元资产的家庭达13.3万户19岁小伙救下5人后溺亡 多方发声315晚会后胖东来又人满为患了张立群任西安交通大学校长“重生之我在北大当嫡校长”男子被猫抓伤后确诊“猫抓病”测试车高速逃费 小米:已补缴周杰伦一审败诉网易网友洛杉矶偶遇贾玲今日春分倪萍分享减重40斤方法七年后宇文玥被薅头发捞上岸许家印被限制高消费萧美琴窜访捷克 外交部回应联合利华开始重组专访95后高颜值猪保姆胖东来员工每周单休无小长假男子被流浪猫绊倒 投喂者赔24万小米汽车超级工厂正式揭幕黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发当地回应沈阳致3死车祸车主疑毒驾恒大被罚41.75亿到底怎么缴妈妈回应孩子在校撞护栏坠楼外国人感慨凌晨的中国很安全杨倩无缘巴黎奥运校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变王树国卸任西安交大校长 师生送别手机成瘾是影响睡眠质量重要因素国产伟哥去年销售近13亿阿根廷将发行1万与2万面值的纸币兔狲“狲大娘”因病死亡遭遇山火的松茸之乡“开封王婆”爆火:促成四五十对奥巴马现身唐宁街 黑色着装引猜测考生莫言也上北大硕士复试名单了德国打算提及普京时仅用姓名天水麻辣烫把捣辣椒大爷累坏了

资讯网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化