Java實現SSH模式加密的實現原理思路分享給大家。
一、SSH加密原理
SSH是先通過非對稱加密告訴服務端一個對稱加密口令,然后進行驗證用戶名和密碼的時候,使用雙方已經知道的加密口令進行加密和解密,見下圖:
解釋:SSH中為什么要使用非對稱加密,又使用對稱加密,到底有什么用處?到底安全不安全?既然后來又使用了對稱加密,開始的時候為什么還要用非對稱加密?反過來,既然用非對稱加密,為什么又要使用對稱加密呢?
非對稱加密,是為了將客戶端產生的256位隨機的口令傳遞到服務端,那么在傳遞的過程中,使用公鑰進行了加密,這樣,這個256位的加密口令就很難被網絡上進行破解。
對稱加密,因為頻繁的使用非對稱加密是非常浪費性能的,那么SSH就是用了256位長度的口令作為接下來傳遞用戶名密碼時的加密口令,其破解的難度,想必大家都知道了,每一位上都有0-9種變化。
這樣安全嗎,我覺得還是很不錯的,具體使用起來也易于讓人理解。
二、我的SSH加密原理
①、使用場景
我所開發的項目是大宗期貨交易,主要服務于交易所,這也就產生一個需求就是,我們需要控制交易所使用我們軟件的周期。也就是說我們的項目留有一個后門,用來控制項目的周期,假如交易所使用軟件的周期到了,那么如果他不續費,而項目的代碼部署在人家的服務器上,此時我們就很難控制了,但是有了這個后門,到期后會自動停止軟件,這樣就不擔心交易所不給我們錢了。
②、使用方式
我們給交易的項目代碼中包含一個后門,該后門通過webservice client發送一個請求到web service。
web service接收到請求后,回給client需要的信息。
在以上這個過程當中,就會產生一個SSH加密的請求方式,請允許我用一個拙劣的圖表示一下。
三、我的SSH具體實現
既然要用到webservice,那么就需要建立web service服務,還有web service client。關于這方面,我暫時不想說太多,方式有很多,我在這就不誤導大家了。我是通過eclipse搞定的,可參照webservice之間通信 。
接下來,我將介紹代碼,但是考慮到篇幅問題,一些不必要的代碼我就不貼出來了,關鍵在于講解清楚這個原理。
①、service
ExchangeService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
public byte [] request(String param, String resultType) { logger.info( "請求參數:" + param); // 返回對象 KeyResult keyResult = new KeyResult(); try { // 先獲取公鑰 if (resultType.equals(PUBLIC_KEY_RESULT_TYPE)) { Map<String, Object> keyMap = RSACoder.initKey(); // 產生公鑰和私鑰 privateKey = RSACoder.getPrivateKey(keyMap); keyResult.setKey(RSACoder.getPublicKey(keyMap)); logger.info( "公鑰字符串:" + keyResult.getKey()); logger.info( "私鑰字符串:" + privateKey); } else if (resultType.equals(ECHOSTR_RESULT_TYPE)) { // 設置客戶端的口令信息 byte [] paramByte = new BASE64Decoder().decodeBuffer(param); echoStr = new String(RSACoder.decryptByPrivateKey(paramByte, privateKey)); } else { // 通過數據庫獲取交易所對應的權限信息. // 先將請求轉換為byte數組,然后再進行解密,最后轉換為字符串 ExchangeInfo info = ExchangeInfo.dao.getInfoByName( new String(CryptUtil.decrypt( new BASE64Decoder().decodeBuffer(param), echoStr.getBytes()))); String result = "" ; // 獲取系統啟用權限 if (resultType.equals(PRIVILEGE_RESULT_TYPE)) { // 先判斷使用權限 // 在判斷使用日期 // 當前登錄用登錄時獲取登錄的當前日期和開始日期進行比較,然后計算還可以使用的日期 long time = ( new Date().getTime() / 1000 ) - string2DateInt(openday); // 換算成天數 int day = ( int ) (time / ( 60 * 60 * 24 )); // 還可以使用的天數 if (usedays - day > 0 ) { // 可以使用 result = "1" ; } else { // 無法使用 result = "0" ; } } keyResult.setResult(CryptUtil.encrypt(result.getBytes(), echoStr.getBytes())); } return JsonUtil.objectToByte(keyResult); } catch (Exception e) { logger.error( "webservice出錯了!!!!" ); logger.error(e.getMessage(), e); } return null ; } |
再贅述一下:
第一個判斷語句中的內容就是生成公鑰和私鑰,并且返回公鑰。
第二個判斷語句中的內容就是保存client發送的隨機字符串,這一步非常關鍵,隨機字符串首先通過公鑰進行了加密,這大大加強了加密的深度。
第三個判斷語句中的內容就是將client的權限通過隨機字符串進行加密。
②、client
ExchangeUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
public static boolean canRunForExchange(String resultType) { int i = 1 ; boolean result = false ; while ( true ) { try { // webservice調用類 ExchangeServiceProxy proxy = new ExchangeServiceProxy(); BASE64Encoder encoder = new BASE64Encoder(); // step1.獲取service產生的公鑰 KeyResult keyResult = JsonUtil.byteToObject(proxy.request( null , PUBLIC_KEY_RESULT_TYPE), KeyResult. class ); // step2.產生隨機字符串,發送到webserivce String echoStr = StrUtil.getEchoStrByLength( 10 ); byte [] echoByteParam = RSACoder.encryptByPublicKey(echoStr.getBytes(), keyResult.getKey()); proxy.request(encoder.encode(echoByteParam), ECHOSTR_RESULT_TYPE); // step3.加密客戶端請求信息,然后發送到webservice // 先加密為byte數組,然后轉換成字符串 byte [] results = proxy.request( encoder.encode(CryptUtil.encrypt(Constants.client_type.getBytes(), echoStr.getBytes())), resultType); keyResult = JsonUtil.byteToObject(results, KeyResult. class ); // step4.通過口令解密服務端返回消息 String response = new String(CryptUtil.decrypt(keyResult.getResult(), echoStr.getBytes())); if (response.equals( "1" )) { result = true ; } break ; } catch (Exception e) { logger.debug( "第" + i + "次加載webservice失敗" ); i++; logger.error(e.getMessage(), e); if (i >= 10 ) { break ; } } } return result; } |
稍作解釋:
通過循環主要為了防止網絡斷開時服務不停的發送請求,最多10次就夠了。
主要有四步操作,注釋中我想解釋的還可以。
③、共享加密解密公共類
CryptUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package com.honzh.socket.util; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec; public class CryptUtil { /** * @Title: encrypt * @Description: 加密 * @param data * @param key * @return * @throws Exception */ public static byte [] encrypt( byte [] data, byte [] key) throws Exception { key = get8(key); Cipher cipher = Cipher.getInstance( "DES/CBC/PKCS5Padding" ); DESKeySpec desKeySpec = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" ); SecretKey secretKey = keyFactory.generateSecret(desKeySpec); IvParameterSpec iv = new IvParameterSpec(key); cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); return cipher.doFinal(data); } /** * @Title: decrypt * @Description: 解密 * @param data * @param key * @return * @throws Exception */ public static byte [] decrypt( byte [] data, byte [] key) throws Exception { key = get8(key); Cipher cipher = Cipher.getInstance( "DES/CBC/PKCS5Padding" ); DESKeySpec desKeySpec = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" ); SecretKey secretKey = keyFactory.generateSecret(desKeySpec); IvParameterSpec iv = new IvParameterSpec(key); cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); return cipher.doFinal(data); } private static byte [] get8( byte [] key) { byte [] key1 = new byte [ 8 ]; for ( int i = 0 ; i < 8 ; i++) { key1[i] = key[i]; } return key1; } public static String toHexString( byte [] data) { String s = "" ; for ( int i = 0 ; i < data.length; i++) { s += Integer.toHexString(data[i] & 0xFF )+ "-" ; } return s; } } |
一般情況下,SHA和MD5兩種加密就夠我們使用了!
至于其他的輔助類我就不多介紹了,網上有很多資源,希望大家可以結合學習。