網(wǎng)絡安全實驗——安全通信軟件safechat的設計
僅供參考,請勿直接抄襲,抄襲者后果自負。
倉庫地址:
后端地址:https://github.com/yijunquan-afk/safechat-server
前端地址: https://github.com/yijunquan-afk/safechat-client
CosUpload.java中的COS設置,需要自己配
1 設計要求
結合所學安全機制設計實現(xiàn)一個簡單的安全通信軟件,包含機密性,消息認證等基本功能。并考慮其中涉及的密鑰分配方式與機密性算法等相關問題的解決.實現(xiàn)方法不限,使用機制不限。
要求:
1、 獨立完成
2、 具有完整的流程設計,報文格式等相關分析。
3、 具備自圓其說的安全性設計思考
2 設計分工
3 設計原理
SHA-2
SHA-2,名稱來自于安全散列算法2(英語:Secure Hash Algorithm 2)的縮寫,一種密碼散列函數(shù)算法標準,由美國國家安全局研發(fā)[3],由美國國家標準與技術研究院(NIST)在2001年發(fā)布。屬于SHA算法之一,是SHA-1的后繼者。其下又可再分為六個不同的算法標準,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。
RSA
RSA加密算法是一種非對稱加密算法,在公開密鑰加密和電子商業(yè)中被廣泛使用。RSA是由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)在1977年一起提出的。當時他們?nèi)硕荚诼槭±砉W院工作。RSA 就是他們?nèi)诵帐祥_頭字母拼在一起組成的。
對極大整數(shù)做因數(shù)分解的難度決定了 RSA 算法的可靠性。換言之,對一極大整數(shù)做因數(shù)分解愈困難,RSA 算法愈可靠。假如有人找到一種快速因數(shù)分解的算法的話,那么用 RSA 加密的信息的可靠性就會極度下降。但找到這樣的算法的可能性是非常小的。今天只有短的 RSA 鑰匙才可能被強力方式破解。到2020年為止,世界上還沒有任何可靠的攻擊RSA算法的方式。只要其鑰匙的長度足夠長,用RSA加密的信息實際上是不能被破解的。
WebSocket協(xié)議
WebSocket是雙向的,在客戶端-服務器通信的場景中使用的全雙工協(xié)議,與HTTP不同,它以ws://或wss://開頭。它是一個有狀態(tài)協(xié)議,這意味著客戶端和服務器之間的連接將保持活動狀態(tài),直到被任何一方(客戶端或服務器)終止。在通過客戶端和服務器中的任何一方關閉連接之后,連接將從兩端終止。
以客戶端-服務器通信為例,每當啟動客戶端和服務器之間的連接時,客戶端-服務器進行握手隨后創(chuàng)建一個新的連接,該連接將保持活動狀態(tài),直到被他們中的任何一方終止。建立連接并保持活動狀態(tài)后,客戶端和服務器將使用相同的連接通道進行通信,直到連接終止。
新建的連接被稱為WebSocket。一旦通信鏈接建立和連接打開后,消息交換將以雙向模式進行,客戶端-服務器之間的連接會持續(xù)存在。如果其中任何一方(客戶端服務器)宕掉或主動關閉連接,則雙方均將關閉連接。套接字的工作方式與HTTP的工作方式略有不同,狀態(tài)代碼101表示W(wǎng)ebSocket中的交換協(xié)議。
JWT
JWT就是通過JSON形式作為Web應用中的令牌,用于在各方之間安全地將信息作為JSON對象傳輸。在數(shù)據(jù)傳輸過程中還可以完成數(shù)據(jù)加密,簽名等相關處理。
基于JWT認證
首先,前端通過Wb表單將自己的用戶名和密碼發(fā)送到后端的接口。這一過程一般是一個HTTP POST請求。
2、后端核對用戶名和密碼成功后,將用戶的id等其他信息作為JWT Payload(負載),將其與頭部分別進行Base64編碼拼接后簽名,形成一個JWT(Token)。形成的JWT就是一個形同11.Zzz.xx的字符串。token head.payload.signature
3、后端將JWT字符串作為登錄成功的返回結果返回給前端。前端可以將返回的結果保存在localStorage或sessionStorage.上,退出登錄時前端刪除保存的JWT即可。
4、前端在每次請求時將JWT放入HTTP Header中的Authorization位。(解決XSS和XSRF問題)
5、后端檢查JWT是否存在,如存在驗證JWT的有效性。檢查簽名是否正確,檢查Token是否過期,檢查Token的接收方是否是自己(可選)
JWT結構
jwt生成的字符串包含有三部分
1、 jwt頭信息部分header:標頭通常由兩部分組成:令牌的類型(即JWT所使用的簽名算法,例如HMAC、SHA256或RSA。它會使用Base64編碼組成JWT結構的第一部分。
2、 在效載荷Payload:令牌的第二部分是有效負載,其中包含聲明。聲明是有關實體(通常是用戶)和其他數(shù)據(jù)的聲明。同樣的,它會使用Ba$64編碼組成JWT結構的第二部分
3、 簽名哈希Signature:header和payload都是結果Base64編碼過的,中間用.隔開,第三部分就是前面兩部分合起來做簽名,密鑰絕對自己保管好,簽名值同樣做Base64編碼拼接在JWT后面。(簽名并編碼)
AES
高級加密標準(英語:Advanced Encryption Standard,縮寫:AES),又稱Rijndael加密法(荷蘭語發(fā)音: [?r?inda?l],音似英文的“Rhine doll”),是美國聯(lián)邦政府采用的一種區(qū)塊加密標準。這個標準用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。經(jīng)過五年的甄選流程,高級加密標準由美國國家標準與技術研究院(NIST)于2001年11月26日發(fā)布于FIPS PUB 197,并在2002年5月26日成為有效的標準。現(xiàn)在,高級加密標準已然成為對稱密鑰加密中最流行的算法之一。
嚴格地說,AES和Rijndael加密法并不完全一樣(雖然在實際應用中兩者可以互換),因為Rijndael加密法可以支持更大范圍的區(qū)塊和密鑰長度:AES的區(qū)塊長度固定為128比特,密鑰長度則可以是128,192或256比特;而Rijndael使用的密鑰和區(qū)塊長度均可以是128,192或256比特。加密過程中使用的密鑰是由Rijndael密鑰生成方案產(chǎn)生。
大多數(shù)AES計算是在一個特別的有限域完成的。
AES加密過程是在一個4×4的字節(jié)矩陣上運作,這個矩陣又稱為“體(state)”,其初值就是一個明文區(qū)塊(矩陣中一個元素大小就是明文區(qū)塊中的一個Byte)。(Rijndael加密法因支持更大的區(qū)塊,其矩陣的“列數(shù)(Row number)”可視情況增加)加密時,各輪AES加密循環(huán)(除最后一輪外)均包含4個步驟:
① AddRoundKey—矩陣中的每一個字節(jié)都與該次回合密鑰(round key)做XOR運算;每個子密鑰由密鑰生成方案產(chǎn)生。
② SubBytes—透過一個非線性的替換函數(shù),用查找表的方式把每個字節(jié)替換成對應的字節(jié)。
③ ShiftRows—將矩陣中的每個橫列進行循環(huán)式移位。
④ MixColumns—為了充分混合矩陣中各個直行的操作。這個步驟使用線性轉(zhuǎn)換來混合每內(nèi)聯(lián)的四個字節(jié)。最后一個加密循環(huán)中省略MixColumns步驟,而以另一個AddRoundKey取代。
4 整體設計方案
網(wǎng)絡協(xié)議
本次設計中,我使用了HTTP協(xié)議處理一般的網(wǎng)絡請求:如登錄、注冊、好友列表獲取、個人信息獲取、頭像更新等功能。
而好友之間點對點的通信,為了持續(xù)快速地溝通,我是用WebSocket協(xié)議來處理信息發(fā)送請求。
客戶端技術選型
客戶端負責的是與用戶進行交互,因此在實用之外還需要考慮到界面美觀整潔,以給用戶帶來良好的使用體驗。因此,前端選擇使用 vue + AntDesign 組件庫進行界面構建。另一方面,由于需要建立 WebSocket 連接,發(fā)送 WebSocket 請求,因此需要引入 WebSocket 相關功能的實現(xiàn)。這里使用的是 socket.io 這一 NodeJS 第三方模塊。
服務端技術選型
對于服務端,采用了 Java + SpringBoot 為大框架來進行服務端的開發(fā)。數(shù)據(jù)庫采用的是經(jīng)典的關系型數(shù)據(jù)庫 MySql。同時為了建立 WebSocket 連接,處理 WebSocket 請求,選擇了 socket.io 的一個 Java 移植版本 netty-socketio。netty-socketio是一個開源的Socket.io服務器端的一個java的實現(xiàn),它基于Netty框架,可用于服務端推送消息給客戶端。
整體功能說明
本系統(tǒng)主要包含六個大的功能模塊:登陸注冊、用戶信息獲取、信息發(fā)送、好友列表顯示、頭像上傳以及退出系統(tǒng)。其中信息發(fā)送是本次課程設計最重要的部分,是安全通信的主要體現(xiàn)。
5 安全加密部分代碼說明
整體設計
HTTP加密
Token產(chǎn)生
private static String sign(String userId,String password){
Algorithm algorithm = Algorithm.HMAC256(password);
String token = JWT.create()
.withClaim(CLAIM_USERID_NAME,userId)
.withExpiresAt(new Date(System.currentTimeMillis()+EXPIRED_TIME/2))
.sign(algorithm);
return token;
}
/**
* 生成一個登錄token
* @param userId
* @param password
* @return
*/
public static String loginSign(String userId,String password){
String token = sign(userId,password);
cache.putToken(token,token);
return token;
}
每次登錄產(chǎn)生Token,并存儲在前端的localStorage中,每次發(fā)送HTTP的POST和GET請求時加在HTTP Header中的Authorization位。(解決XSS和XSRF問題)
Token認證
后端接收HTTP請求時需要認證Token。
如此做可以認證發(fā)送HTTP請求的用戶身份,適用于所有HTTP請求
/**
* 驗證客戶端傳來token是否有效
* 驗證邏輯順序如下:
* 1. token是否為空
* 2. token中賬號是否存在
* 3. 根據(jù)token中賬號從數(shù)據(jù)庫中獲取真實密碼等用戶信息,并驗證用戶信息是否有效
*/
public static void verifyToken(String clientToken, stu.software.chatroom.common.CommonService commonService){
if(!StringUtils.hasText(clientToken)){
//token為空
throw new RuntimeException("無登錄令牌!");
}
//從客戶端登錄令牌中獲取當前用戶賬號
String userId = JWT.decode(clientToken).getClaim(CLAIM_USERID_NAME).asString();
if(!StringUtils.hasText(userId)){
//token中賬號不存在
throw new RuntimeException("登錄令牌失效!");
}
//取出緩存中的登錄令牌
String cacheToken = cache.getToken(clientToken);
if(!StringUtils.hasText(cacheToken)){
//緩存中沒有登錄令牌
throw new RuntimeException("登錄令牌失效!");
}
User user = commonService.getUserById(userId);
if(user==null){
//用戶不存在
throw new RuntimeException("用戶不存在!");
}
//驗證Token有效性
try{
Algorithm algorithm = Algorithm.HMAC256(user.getU_pwd());
JWTVerifier jwtVerifier = JWT.require(algorithm).withClaim(CLAIM_USERID_NAME,userId).build();//構建驗證器
jwtVerifier.verify(cacheToken);
}catch(TokenExpiredException e){
//令牌過期,刷新令牌
String newToken = sign(userId,user.getU_pwd());
cache.putToken(clientToken,newToken);
}catch(Exception e){
e.printStackTrace();
//令牌驗證未通過
throw new RuntimeException("令牌錯誤!請登錄。");
}
注冊密碼加密
使用SHA256加密注冊時用戶使用的密碼,數(shù)據(jù)庫中存的是密文,這樣可防止數(shù)據(jù)庫被攻擊導致密碼泄露。
/***
* 利用Apache的工具類實現(xiàn)SHA-256加密
* @return str 加密后的報文
*/
public static String getSHA256Str(String str) {
MessageDigest messageDigest;
String encodeSir = str;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
encodeSir = Hex.encodeHexString(hash);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return encodeSir;
}
/**
* 通過該方法將密碼加密(實際上并沒有)
*/
public static String encodePwd(String u_pwd) {
// 密碼通過此方法解密并再加密
return getSHA256Str(u_pwd);
}
登錄密碼加密
登錄時,前端輸入明文密碼,使用SHA256加密該密碼以后,再加數(shù)據(jù)發(fā)送到后端。后端根據(jù)該加密后的密碼與數(shù)據(jù)庫比對,從而驗證用戶身份。
此做法避免了前端請求數(shù)據(jù)被攔截導致密碼泄露。
import { sha256 } from 'js-sha256';
/**
* 加密方法
*/
export function PASSWORD(str) {
let encodedStr = str;
encodedStr = sha256(encodedStr);
return encodedStr;
}
const login = () => {
post("/user/login", {
u_name: u_name.value,
u_pwd: PASSWORD(u_pwd.value),
})
.then((res) => {
tip.success(res.message);
let token = res.data;
setLocalToken(token);
router.push({ name: "Room", query: { usr: u_name.value } });
})
.catch((err) => {
tip.error("賬號密碼錯誤!");
});
};
密鑰分配——使用Keytool
參考教程 https://blog.csdn.net/m0_59579040/article/details/124811147
keytool 是個密鑰和證書管理工具。它使用戶能夠管理自己的公鑰/私鑰對及相關證書,用于(通過數(shù)字簽名)自我認證(用戶向別的用戶/服務認證自己)或數(shù)據(jù)完整性以及認證服務。它還允許用戶儲存他們的通信對等者的公鑰(以證書形式)。
在計算機網(wǎng)絡上,OpenSSL是一個開放源代碼的軟件庫包,應用程序可以使用這個包來進行安全通信,避免竊聽,同時確認另一端連接者的身份。這個包廣泛被應用在互聯(lián)網(wǎng)的網(wǎng)頁服務器上。
通過如下步驟可以產(chǎn)生證書和公鑰
keytool -genkeypair -storetype PKCS12 -alias yjq - -keyalg RSA -keysize 1024 -dname "CN=xxx, OU=xxx, O=xxx, L=xx, ST=xx, C=CN" -keystore D:\mygit\大三下筆記\網(wǎng)安課設\safechat-server\src\main\resources\keys-and-certs\yjq.keystore -keypass 123456 -storepass 123456 -validity 36500 -v
產(chǎn)生二進制文件yjq.keystore,以上部分可由腳本生成。
經(jīng)過KeyStore的相關操作生成公鑰、證書和私鑰
當用戶需要公鑰和私鑰時,只需要調(diào)用相關方法即可。
public static void genKeyPair(String name) throws Exception {
//以 PKCS12 規(guī)格,創(chuàng)建 KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
path = "keys-and-certs/" + name + ".keystore";
//載入 jks 和該 jks 的密碼 到 KeyStore 內(nèi)
keyStore.load(new FileInputStream(new ClassPathResource("keys-and-certs/yjq.keystore").getFile()), "123456".toCharArray());
// 要獲取 key,需要提供 KeyStore 的別名 和該 KeyStore 的密碼
// 獲取 keyStore 內(nèi)所有別名 alias
Enumeration<String> aliases = keyStore.aliases();
String alias = null;
alias = aliases.nextElement();
char[] keyPassword = "123456".toCharArray();
keyPairString.clear();
//私鑰
privateKey = (PrivateKey) keyStore.getKey(alias, keyPassword);
keyPairString.put("PR", new String(Base64.getEncoder().encode(privateKey.getEncoded())));
//證書
Certificate certificate = keyStore.getCertificate(alias);
//公鑰
publicKey = certificate.getPublicKey();
keyPairString.put("PU", new String(Base64.getEncoder().encode(publicKey.getEncoded())));
}
使用公鑰加密保證消息認證和機密性
參考教程https://blog.csdn.net/m0_59579040/article/details/124811147.
A和B進行通信,首先使用A的私鑰對報文M進行加密——數(shù)字簽名;然后A用B的公鑰對上述結果進行加密——保證了保密性。
B收到消息后,用B的私鑰解密,再用A的公鑰驗證簽名。
這里我使用RSA作為加密算法、SHA1WithRSA作為簽名算法,簽名和加密的操作實現(xiàn)在類RSAUtils.java中。
簽名
/**
* 私鑰簽名
* @param content 字符串
* @param priKey 私鑰
* @return
* @throws Exception
*/
public static byte[] sign(String content, PrivateKey priKey) throws Exception {
Signature signature = Signature.getInstance(SIGALG);
signature.initSign(priKey);
signature.update(content.getBytes());
return signature.sign();
}
/**
* 公鑰驗證簽名
* @param content 字符串
* @param sign 簽名
* @param pubKey 公鑰
* @return 身份是否真實
* @throws Exception
*/
public static boolean verify(String content, byte[] sign, PublicKey pubKey) throws Exception {
Signature signature = Signature.getInstance(SIGALG);
signature.initVerify(pubKey);
signature.update(content.getBytes());
return signature.verify(sign);
}
加密解密
/**
* RSA公鑰加密
*
* @param content 加密字符串
* @param publicKey 公鑰
* @return 密文
* @throws Exception 加密過程中的異常信息
*/
public static String encrypt(String content, String publicKey) throws Exception {
//base64編碼的公鑰
byte[] decoded = Base64.getMimeDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEYALG).generatePublic(new X509EncodedKeySpec(decoded));
System.out.println(pubKey.getAlgorithm());
//RSA加密
Cipher cipher = Cipher.getInstance(KEYALG);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes("UTF-8")));
return outStr;
}
/**
* RSA私鑰解密
*
* @param content 加密字符串
* @param privateKey 私鑰
* @return 明文
* @throws Exception 解密過程中的異常信息
*/
public static String decrypt(String content, String privateKey) throws Exception {
//64位解碼加密后的字符串
byte[] inputByte = Base64.getMimeDecoder().decode(content);
// //base64編碼的私鑰
byte[] decoded = Base64.getMimeDecoder().decode(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
使用AES加密消息
因為公鑰加密的消息認證比較費時間,所以當兩個用戶建立消息通信時由一方產(chǎn)生會話密鑰,使用公鑰加密來傳送會話密鑰并認證身份。身份認證完成后,使用該會話密鑰加密消息,其中使用對稱加密技術AES加密消息。
消息報文格式如下:
1、 id:報文標識id;
2、 time:報文發(fā)送時間
3、 content:報文內(nèi)容(加密)
4、 type:報文類型:會話密鑰消息/公鑰消息
5、 sender_name:發(fā)送者
6、 receiver_name:接收者
7、 sign:發(fā)送者簽名。
加密過程如下:
public final class AESUtils{
private static final String ALGORITHM = "AES";
public static String genAesSecret(){
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
//下面調(diào)用方法的參數(shù)決定了生成密鑰的長度,可以修改為128, 192或256
kg.init(256);
SecretKey sk = kg.generateKey();
byte[] b = sk.getEncoded();
String secret = Base64.encodeBase64String(b);
return secret;
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("沒有此算法");
}
}
/**
* 根據(jù)密鑰對指定的明文plainText進行加密.
*
* @param plainBytes 明文
* @param keyBytes 密碼
* @return 加密后的密文.
* @since 0.0.8
*/
public static byte[] encrypt(byte[] plainBytes, byte[] keyBytes) {
try {
SecretKey secretKey = getSecretKey(keyBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plainBytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 根據(jù)密鑰對指定的密文 cipherBytes 進行解密.
*
* @param cipherBytes 加密密文
* @param keyBytes 秘鑰
* @return 解密后的明文.
* @since 0.0.8
*/
public static byte[] decrypt(byte[] cipherBytes, byte[] keyBytes) {
try {
SecretKey secretKey = getSecretKey(keyBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(cipherBytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 獲取加密 key
* @param keySeed seed
* @return 結果
* @since 0.0.8
*/
private static SecretKey getSecretKey(byte[] keySeed) {
try {
// 避免 linux 系統(tǒng)出現(xiàn)隨機的問題
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(keySeed);
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(secureRandom);
return generator.generateKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
服務端加密
結合RSA與AES的加密如下:
先用公鑰加密RSA發(fā)送對稱加密使用的會話密鑰,然后再用會話密鑰進行AES對稱加密通信。
// 監(jiān)聽客戶端發(fā)送消息
socketIOServer.addEventListener(Constants.EVENT_MESSAGE_TO_SERVER, String.class, (client, data, ackSender) -> {
String sender_name = getParamsByClient(client, "u_name");
ObjectMapper mapper = new ObjectMapper();
Message message = mapper.readValue(data, Message.class);
String receiver_name = message.getReceiver_name();
if (message.getType().equals(Constants.MASTER_MESSAGE)) {
//使用公鑰加密傳送會話密鑰
if (AesKey.equals("")) {
log.info("用戶" + sender_name + "生成會話密鑰");
AesKey = AESUtils.genAesSecret();
message.setContent(AesKey);
log.info("用戶" + sender_name + "使用用戶" + sender_name + "的私鑰對會話密鑰進行簽名");
String sign = new String(RSAUtils.sign(message.getContent(), RSAUtils.getPrivateKey()), "ISO-8859-1");
message.setSign(sign);
String result = RSAUtils.encrypt(message.getContent(), publicKeyStringMap.get(receiver_name));
log.info("使用用戶" + receiver_name + "的公鑰對會話密鑰進行加密:" + result);
message.setContent(result);
sendMessageToFriend(message.getReceiver_name(), message);
} else {
return;
}
} else {
//使用會話密鑰發(fā)送消息
byte[] bytes = AESUtils.encrypt(message.getContent().getBytes(), AesKey.getBytes());
String encrypt = new String(bytes, "ISO-8859-1");
log.info("用戶" + sender_name + "使用會話密鑰加密消息");
message.setContent(encrypt);
sendMessageToFriend(message.getReceiver_name(), message);
}
});
//
//GBK, GB2312,UTF-8等一些編碼方式為多字節(jié)或者可變長編碼,原來的字節(jié)數(shù)組就被改變了,再轉(zhuǎn)回原來的byte[]數(shù)組就會發(fā)生錯誤了。
//ISO-8859-1通常叫做Latin-1,Latin-1包括了書寫所有西方歐洲語言不可缺少的附加字符,其中 0~127的字符與ASCII碼相同,
// 它是單字節(jié)的編碼方式,在來回切換時不會出現(xiàn)錯誤。
// 監(jiān)聽客戶端接收消息
socketIOServer.addEventListener("receive_triger", String.class, (client, data, ackSender) -> {
ObjectMapper mapper = new ObjectMapper();
Message message = mapper.readValue(data, Message.class);
String sender_name = message.getSender_name();
String receiver_name = message.getReceiver_name();
if (message.getType().equals(Constants.MASTER_MESSAGE)) {
log.info("收到來自" + sender_name + "發(fā)送給" + message.getReceiver_name() + "的消息: " + message.getContent());
String result = RSAUtils.decrypt(message.getContent(), RSAUtils.getKeyPair().get("PR"));
log.info("用戶" + receiver_name + "使用用戶" + receiver_name + "的私鑰對消息進行解密:");
message.setContent(result);
log.info("用戶" + receiver_name + "使用用戶" + sender_name + "的公鑰對消息進行驗證簽名");
Boolean sign = (RSAUtils.verify(message.getContent(), message.getSign().getBytes("ISO-8859-1"), publicKeyMap.get(sender_name)));
if (sign) {
log.info("簽名驗證成功!身份無誤");
} else {
throw new Exception("簽名錯誤!");
}
receiveMessageFromFriend(message.getReceiver_name(), message);
} else {
log.info("收到來自" + sender_name + "發(fā)送給" + message.getReceiver_name() + "的消息: " + message.getContent());
String text = new String(AESUtils.decrypt(message.getContent().getBytes("ISO-8859-1"), AesKey.getBytes()), "UTF-8");
log.info("用戶" + receiver_name + "使用會話密鑰進行解密");
message.setContent(text);
receiveMessageFromFriend(message.getReceiver_name(), message);
}
});
6 演示
登錄
進入主頁面
可以看到好友列表
同時獲取本地密鑰庫中的公私鑰并將其加入公鑰庫
選擇好友進行私聊
選擇好友進行私聊,進入聊天界面。
發(fā)送消息
在輸入框中輸入消息,點擊發(fā)送,接收者和發(fā)送者的聊天框都會出現(xiàn)相應的消息。此消息是經(jīng)過后端AES對稱加密解密得到的。文章來源:http://www.zghlxwxcb.cn/news/detail-816760.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-816760.html
到了這里,關于網(wǎng)絡安全實驗——安全通信軟件safechat的設計的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!