RSA加密演算法的簡單案例

来源:http://www.cnblogs.com/laoA188/archive/2016/08/16/5775900.html
-Advertisement-
Play Games

RSA加密演算法是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊。 ...


RSA加密演算法是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊。

那關於RSA加密演算法有哪些應用呢?以下舉一個資料庫身份驗證的案例。

在使用數據集進行身份認證時,密碼存在資料庫中,認證時用戶輸入的密碼與資料庫中密碼相同則認證通過,若資料庫被破解了則對系統造成威脅,怎樣保證系統安全呢?這裡就可以應用RSA加密演算法,對許可權加密。

思路:

就是在url中傳用戶名密碼時,先把用戶名進行翻轉,然後再進行加密,如輸入的密碼為12,實際後臺進行加密的值為21,再與資料庫進行驗證,這樣就可以避免資料庫被破解查看到的是21的加密碼,登陸系統時以21是無法登陸成功的。

以報表軟體FineReport為例,這是一個能讀取各類資料庫的報表軟體,分客戶端和前端展示。

實現方案:

1、把RSA加密使用的第三方包,放到工程web-inf/lib文件夾下即可。

2、調用js文件

RSA文件夾為前端js加密時需要調用js文件,因此需要將Barrett.js、BigInt.js、RSA.js放到工程目錄下如:WebReport/js,新建js文件夾放入js文件。

3、定義RSA加密類

定義RSAUtil.java類文件,先運行類中generateKeyPair()方法,會在伺服器D盤中生成一個隨機的RSAKey.txt文件,保存公鑰和密鑰,每訪問一次這個方法會刷新一次txt文件。

package com.fr.privilege;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

/**
 * RSA 工具類。提供加密,解密,生成密鑰對等方法。
 * 需要到http://www.bouncycastle.org下載bcprov-jdk14-123.jar。
 * 
 */
public class RSAUtil {
	/**
	 * * 生成密鑰對 *
	 * 
	 * @return KeyPair *
	 * @throws EncryptException
	 */
	public static KeyPair generateKeyPair() throws Exception {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final int KEY_SIZE = 1024;// 沒什麼好說的了,這個值關係到塊加密的大小,可以更改,但是不要太大,否則效率會低
			keyPairGen.initialize(KEY_SIZE, new SecureRandom());
			KeyPair keyPair = keyPairGen.generateKeyPair();
			saveKeyPair(keyPair);
			return keyPair;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	public static KeyPair getKeyPair() throws Exception {
		FileInputStream fis = new FileInputStream("C:/RSAKey.txt");
		ObjectInputStream oos = new ObjectInputStream(fis);
		KeyPair kp = (KeyPair) oos.readObject();
		oos.close();
		fis.close();
		return kp;
	}

	public static void saveKeyPair(KeyPair kp) throws Exception {

		FileOutputStream fos = new FileOutputStream("C:/RSAKey.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		// 生成密鑰
		oos.writeObject(kp);
		oos.close();
		fos.close();
	}

	/**
	 * * 生成公鑰 *
	 * 
	 * @param modulus *
	 * @param publicExponent *
	 * @return RSAPublicKey *
	 * @throws Exception
	 */
	public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
			byte[] publicExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}

		RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
				modulus), new BigInteger(publicExponent));
		try {
			return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * * 生成私鑰 *
	 * 
	 * @param modulus *
	 * @param privateExponent *
	 * @return RSAPrivateKey *
	 * @throws Exception
	 */
	public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
			byte[] privateExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}

		RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(
				modulus), new BigInteger(privateExponent));
		try {
			return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * * 加密 *
	 * 
	 * @param key
	 *            加密的密鑰 *
	 * @param data
	 *            待加密的明文數據 *
	 * @return 加密後的數據 *
	 * @throws Exception
	 */
	public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();// 獲得加密塊大小,如:加密前數據為128個byte,而key_size=1024
			// 加密塊大小為127
			// byte,加密後為128個byte;因此共有2個加密塊,第一個127
			// byte第二個為1個byte
			int outputSize = cipher.getOutputSize(data.length);// 獲得加密塊加密後塊大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
					: data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i
							* outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i
							* blockSize, raw, i * outputSize);
				// 這裡面doUpdate方法不可用,查看源代碼後發現每次doUpdate後並沒有什麼實際動作除了把byte[]放到
				// ByteArrayOutputStream中,而最後doFinal的時候才將所有的byte[]進行加密,可是到了此時加密塊大小很可能已經超出了
				// OutputSize所以只好用dofinal方法。

				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * * 解密 *
	 * 
	 * @param key
	 *            解密的密鑰 *
	 * @param raw
	 *            已經加密的數據 *
	 * @return 解密後的明文 *
	 * @throws Exception
	 */
	public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;

			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * * *
	 * 
	 * @param args *
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair()
				.getPublic();
		String test = "hello world";
		byte[] en_test = encrypt(getKeyPair().getPublic(), test.getBytes());
		System.out.println("123:" + new String(en_test));
		byte[] de_test = decrypt(getKeyPair().getPrivate(), en_test);
		System.out.println(new String(de_test));
	}
}

4、定義密碼驗證類

定義TestPasswordValidatorRSA.java密碼驗證類

定義一個類,命名為TestPasswordValidatorRSA.java,擴展於AbstractPasswordValidator,重寫其中密碼驗證方法encodePassword,先把輸入的密碼進行翻轉,然後再進行加密,返回密碼進行驗證,具體代碼如下:

package com.fr.privilege;  

import com.fr.privilege.providers.dao.AbstractPasswordValidator;  
public class TestPasswordValidatorRSA extends AbstractPasswordValidator{  
    //@Override
    public String encodePassword( String clinetPassword) {
    	try {
    		//對密碼進行翻轉如輸入ab翻轉後為ba
    		StringBuffer sb = new StringBuffer();  
	        sb.append(new String(clinetPassword));
	        String bb = sb.reverse().toString();
    		//進行加密
    		byte[] en_test = RSAUtil.encrypt(RSAUtil.getKeyPair().getPublic(),bb.getBytes());     		
    		//進行解密,如果資料庫裡面保存的是加密碼,則此處不需要進行解密
    		byte[] de_test = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_test);  
    		//返回加密密碼
    		clinetPassword=new String(de_test);		
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return clinetPassword; //即獲取加密密碼再與資料庫密碼匹配。  
    }

	@Override
	public boolean validatePassword(String arg0, String arg1) {
		// TODO Auto-generated method stub
		return false;
	}


}

5、編譯類文件

首先編譯RSAUtil.java類文件在伺服器的D盤生成RSAKey.txt文件,再編譯TestPasswordValidatorRSA.java類,把編譯後的class文件放到項目工程web-inf/classes/com/fr/privilege文件夾中。

6、登陸Login.jsp頁面設置

客戶端請求到登錄頁面,隨機生成一字元串,此隨機字元串作為密鑰加密密碼,如下代碼:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="com.fr.privilege.providers.dao.RSAUtil"%>
<%!public String Testmo() {
		String module = "";
		try {
			java.security.interfaces.RSAPublicKey rsap = (java.security.interfaces.RSAPublicKey) RSAUtil
					.getKeyPair().getPublic();
			module = rsap.getModulus().toString(16);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return module;
	}%>
<%!public String Testem() {
		String empoent = "";
		try {
			java.security.interfaces.RSAPublicKey rsap = (java.security.interfaces.RSAPublicKey) RSAUtil
					.getKeyPair().getPublic();
			empoent = rsap.getPublicExponent().toString(16);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return empoent;
	}%>
<html>
	<head>
		<script type="text/javascript"
			src="ReportServer?op=emb&resource=finereport.js"></script>
		<script type="text/javascript" src="js/RSA.js"></script>
		<script type="text/javascript" src="js/BigInt.js"></script>
		<script type="text/javascript" src="js/Barrett.js"></script>
		<script type="text/javascript">    
	function bodyRSA()  
	{  
    setMaxDigits(130);  
    var a = "<%=Testmo()%>";
	var b = "<%=Testem()%>";
	key = new RSAKeyPair(b,"",a);
	} 
function doSubmit() {   
	bodyRSA(); 
    var username = FR.cjkEncode(document.getElementById("username").value); //獲取輸入的用戶名    
    var password = FR.cjkEncode(document.getElementById("password").value);  //獲取輸入的參數    
    $.ajax({    
        url : "ReportServer?op=auth_login&fr_username=" + username + "&fr_password=" + password,   //將用戶名和密碼發送到報表認證地址op=auth_login   
        data : {__redirect__ : 'false'},        
        complete : function(res) {    
            var jo = FR.jsonDecode(res.responseText);    
            if(jo.url) {    
               window.location=jo.url+ "&_=" + new Date().getTime();   //認證成功跳轉頁面,因為ajax不支持重定向所有需要跳轉的設置  
            }    
            else{    
               alert("用戶名密碼錯誤!")  //認證失敗    
            }    
        }    
    })    
}    
</script>
	</head>
	<body>
		<p>
			請登錄
		</p>
		<form name="login" method="POST">
			<p>
				用戶名:
				<input id="username" type="text" />
			</p>
			<p>
				密 碼:
				<input id="password" type="password" />
			</p>
			<input type="button" value="登錄" onclick="doSubmit()"  />
		</form>
	</body>
</html>

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Array類型是ECMAScript中最常用的引用類型。ECMAScript中的數據與其它大多數語言中的數組有著相當大的區別。雖然ECMAScript中的數據與其它語言中的數組一樣都是數據的有序列表,但不同的是,ECMAScript數組中的每一項可以保存任何類型的數據,無論是數值、字元串或者是對象。 ...
  • 這次的分享,主要還是想跟大家聊聊Javascript語言中很重要的概念之一,對象。為什麼說之一呢?因為Javascript其他重要概念還包括:作用域 作用域鏈 繼承 閉包 函數 繼承 數組 ...... 有機會會跟大家分享這些概念的。以下的介紹會分為如下:1:前言2:概述 2.1:對象創建 2.2: ...
  • 今天我又碼了兩個特效:一個是用原生input[type=range]的,另一個完全自定義的;下麵是完整代碼和演示: <!DOCTYPE html tip{ position: absolute; top: 30px; left: 0; right: 0; width: 200px; height: ...
  • DOM技術實現競賽題頁面 這一段時間學習了DOM操作和JS開發,我就自己開發一個競賽題的頁面。 一、業務需求 1、目標:做一個一百道選擇題的頁面 2、功能: 顯示題目和選項。 下一題上一題的按鈕,到第一題和第一百題時停止。 答對了顯示笑臉和笑話,答錯了顯示苦臉和正確答案。 二、開發思路 採用自頂向下 ...
  • 雖然我們的js將來會被很多的礦街(jQuery)取代,但js,我們現在依然不得不學,因為用途很廣泛 ...
  • 使用: 圖片: ...
  • canvas元素 可被用來通過腳本(通常是JavaScript)繪製圖形。比如,它可以被用來繪製圖形,製作圖片集合,甚至用來實現動畫效果。你可以(也應該)在元素標簽內寫入可提供替代的的代碼內容,這些內…容將會在在舊的、不支持canvas元素的瀏覽器或是禁用了JavaScript的瀏覽器內渲染並展現。 ...
  • 這是一款使用純CSS3製作的單頁切換導航菜單界面設計效果。該頁面效果中,在頁面的左側垂直排放一組導航按鈕,當點擊導航按鈕時,相應的頁面會從屏幕右側滑動出來,效果非常炫酷。 線上預覽 源碼下載 使用方法 該單頁切換導航菜單界面的HTML結構如下: 1 2 3 4 5 6 7 8 9 10 11 12 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...