日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

引言

 

在日常開發中我們對HTTP數據傳輸并不陌生,前端需要與后端進行數據交互往往需要調用后端某個API,然而在前端與后端的請求過程中數據真的安全嗎?下面了解一件關于0.01購買phone事件;

事件

在之前報導一個新聞,某個電商App應用因為程序Bug,被灰色產業0.01元擼走千臺iphone手機,損失近千萬,后來了解到也并非高手所為,其實就是該APP在購買接口中對數據傳輸缺少安全防范,通過HTTP抓包工具對傳輸中的數據進行修改,將原本幾千元的iPhone手機價格修改為0.01進行購買;


案例

看完以上事件部分人可能還是不太抓包的概念,所謂抓包就是依賴某個工具,對客戶端與服務端進行請求、或者對服務端對客戶端響應時過程中進行攔截,可以將其請求數據或響應數據進行篡改;

為了能更好了解,這里我準備了一個模擬購買接口:

Java中數據傳輸加密與簽名那些事

 

這里模擬前端正常調用購買接口;

http://localhost:8080/test?money=6000

按理后端應該會輸出6000,這里我利用fiddle抓包工具對該請求進行攔截,將money參數改為1;

Java中數據傳輸加密與簽名那些事

 


Java中數據傳輸加密與簽名那些事

 

成功將合法價格改為非法價格,如果后端接口驗證不注意即會按照該金額生成訂單,則支付1元即可買到高額商品;

 

原由

導致以上事件發生無非就兩種情況:

1.對敏感數據使用明文傳輸;

2.在前端進行校驗后,后端接口并未對數據第二次校驗;

 

如何解決?

在數據傳輸過程中對敏感數據進行加密,即使請求被截獲,也只能獲取到密文信息,無法篡改具體需要修改的數據;

傳輸過程中使用的加密方式通常分為兩類:對稱加密/非對稱加密;

對稱加密:市場上用的比較多的對稱加密有DES、AES、3DES等,對稱加密的流程是通過生成一把秘鑰,分別存儲客戶端以及服務端各自一份,客戶端在請求服務端前先將傳輸的數據通過該秘鑰進行加密,服務端接收到請求之后,先將數據用該秘鑰進行解密再進行處理,從而避免數據在傳輸中被篡改;

Java中數據傳輸加密與簽名那些事

 

缺點:該秘鑰在客戶端加密與服務端解密使用的是同一把,而存儲在客戶端的秘鑰并無法避免泄漏的風險,通過反編譯等手段獲取到秘鑰,那么傳輸過程中攔截請求則照樣可以解密密文數據進行篡改;

Des加密案例:

 

public class DesDecrypt {  
  //加密字段   
  private static String src = "JAVA資料社區"; 

	public static void main(String[] args) { 
    jdkDES(); 
    bcDES();    
  }   
/*JDK實現*/   
public static void jdkDES() { 
  try {  
    //生成KEY           
    KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");  
    keyGenerator.init(56); 
    SecretKey secretKey = keyGenerator.generateKey();  
    byte[] bytesKey = secretKey.getEncoded();  
    //KEY轉換       
    DESKeySpec desKeySpec = new DESKeySpec(bytesKey);    
    SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");           
    Key convertSecretKey = factory.generateSecret(desKeySpec);          
    //加密        
    Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");     
    cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);    
    byte[] result = cipher.doFinal(src.getBytes());     
    System.out.println("jdk des encrypt : " + new String(Hex.encode(result)));     
    //解密      
    cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);      
    result = cipher.doFinal(result);  
    System.out.println("jdk des decrypt : " + new String(result).toString()); 
  } catch (Exception e) {        
    e.printStackTrace();   
  }   
}   

/*BC實現*/  
public static void bcDES() {  
  try {  
    Security.addProvider(new BouncyCastleProvider());   
    //生成KEY  
    KeyGenerator keyGenerator = KeyGenerator.getInstance("DES", "BC");      
    keyGenerator.getProvider();         
    keyGenerator.init(56);    
    SecretKey secretKey = keyGenerator.generateKey();    
    byte[] bytesKey = secretKey.getEncoded();     
    //KEY轉換        
    DESKeySpec desKeySpec = new DESKeySpec(bytesKey);         
    SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");       
    Key convertSecretKey = factory.generateSecret(desKeySpec);     
    //加密     
    Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");  
    cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);   
    byte[] result = cipher.doFinal(src.getBytes());       
    System.out.println("bc des encrypt : " + new String(Hex.encode(result)));     
    //解密        
    cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);       
    result = cipher.doFinal(result);  
    System.out.println("bc des decrypt : " + new String(result).toString()); 
  } catch (Exception e) {   
    e.printStackTrace();      
  }    }
}//運行結果//jdk des encrypt : ab6afc9a81584573cf2f50ceccd28628bd10885ebd84f37c
//jdk des decrypt : Java資料社區
//bc des encrypt : 34fd3ed3d0b6d5ab7baf6db300420f3c4094a2061bb2dd34
//bc des decrypt : Java資料社區

 

非對稱加密:市場上用的比較多的通常為RSA加密,RSA加密的流程與對稱加密不同,相對于更加安全,通過生成兩把秘鑰(私鑰/公鑰),公鑰通常用來對傳輸過程中的數據進行加密,私鑰則用來對加密數據進行解密,公鑰加密的內容只能對應的私鑰才能解密,私鑰加密的內容只能由對應的公鑰解密,通??蛻舳舜鎯€、服務端則存儲私鑰,這樣,即使公鑰被泄漏也沒法破解密文信息;

Java中數據傳輸加密與簽名那些事

 

 

缺點:相對對稱加密性能方面要慢很多,非敏感信息不建議使用;

RSA案例:

public class RSAUtils {
    protected static final Log log = LogFactory.getLog(RSAUtils.class);
    private static String KEY_RSA_TYPE = "RSA";
    private static int KEY_SIZE = 1024;//JDK方式RSA加密最大只有1024位
    private static int ENCODE_PART_SIZE = KEY_SIZE/8;
    public static final String PUBLIC_KEY_NAME = "public";
    public static final String PRIVATE_KEY_NAME = "private";

    public static void main(String[] args) {
        Map<String,String> map=RSAUtils.createRSAKeys(); //創建公私鑰
        String str="Java資料社區";
        String enStr=encode(str,map.get("public"));//公鑰加密
        System.out.println("加密后:"+enStr);
        String deStr=decode(enStr,map.get("private"));
        System.out.println("解密后:"+deStr);
    }

    /**
     * 創建公鑰秘鑰
     * @return
     */
    public static Map<String,String> createRSAKeys(){
        Map<String,String> keyPairMap = new HashMap<>();//里面存放公私秘鑰的Base64位加密
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_RSA_TYPE);
            keyPairGenerator.initialize(KEY_SIZE,new SecureRandom());
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
 
            //獲取公鑰秘鑰
            String publicKeyValue = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
            String privateKeyValue = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
 
            //存入公鑰秘鑰,以便以后獲取
            keyPairMap.put(PUBLIC_KEY_NAME,publicKeyValue);
            keyPairMap.put(PRIVATE_KEY_NAME,privateKeyValue);
        } catch (NoSuchAlgorithmException e) {
            log.error("當前JDK版本沒找到RSA加密算法!");
            e.printStackTrace();
        }
        return keyPairMap;
    }
 
    /**
     * 公鑰加密
     * 描述:
     *     1字節 = 8位;
     *     最大加密長度如 1024位私鑰時,最大加密長度為 128-11 = 117字節,不管多長數據,加密出來都是 128 字節長度。
     * @param sourceStr
     * @param publicKeyBase64Str
     * @return
     */
    public static String encode(String sourceStr,String publicKeyBase64Str){
        byte [] publicBytes = Base64.decodeBase64(publicKeyBase64Str);
        //公鑰加密
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
        List<byte[]> alreadyEncodeListData = new LinkedList<>();
 
        int maxEncodeSize = ENCODE_PART_SIZE - 11;
        String encodeBase64Result = null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE);
            cipher.init(Cipher.ENCRYPT_MODE,publicKey);
            byte[] sourceBytes = sourceStr.getBytes("utf-8");
            int sourceLen = sourceBytes.length;
            for(int i=0;i<sourceLen;i+=maxEncodeSize){
                int curPosition = sourceLen - i;
                int tempLen = curPosition;
                if(curPosition > maxEncodeSize){
                    tempLen = maxEncodeSize;
                }
                byte[] tempBytes = new byte[tempLen];//待加密分段數據
                System.arraycopy(sourceBytes,i,tempBytes,0,tempLen);
                byte[] tempAlreadyEncodeData = cipher.doFinal(tempBytes);
                alreadyEncodeListData.add(tempAlreadyEncodeData);
            }
            int partLen = alreadyEncodeListData.size();//加密次數
 
            int allEncodeLen = partLen * ENCODE_PART_SIZE;
            byte[] encodeData = new byte[allEncodeLen];//存放所有RSA分段加密數據
            for (int i = 0; i < partLen; i++) {
                byte[] tempByteList = alreadyEncodeListData.get(i);
                System.arraycopy(tempByteList,0,encodeData,i*ENCODE_PART_SIZE,ENCODE_PART_SIZE);
            }
            encodeBase64Result = Base64.encodeBase64String(encodeData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encodeBase64Result;
    }
 
    /**
     * 私鑰解密
     * @param sourceBase64RSA
     * @param privateKeyBase64Str
     */
    public static String decode(String sourceBase64RSA,String privateKeyBase64Str){
        byte[] privateBytes = Base64.decodeBase64(privateKeyBase64Str);
        byte[] encodeSource = Base64.decodeBase64(sourceBase64RSA);
        int encodePartLen = encodeSource.length/ENCODE_PART_SIZE;
        List<byte[]> decodeListData = new LinkedList<>();//所有解密數據
        String decodeStrResult = null;
        //私鑰解密
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateBytes);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE);
            cipher.init(Cipher.DECRYPT_MODE,privateKey);
            int allDecodeByteLen = 0;//初始化所有被解密數據長度
            for (int i = 0; i < encodePartLen; i++) {
                byte[] tempEncodedData = new byte[ENCODE_PART_SIZE];
                System.arraycopy(encodeSource,i*ENCODE_PART_SIZE,tempEncodedData,0,ENCODE_PART_SIZE);
                byte[] decodePartData = cipher.doFinal(tempEncodedData);
                decodeListData.add(decodePartData);
                allDecodeByteLen += decodePartData.length;
            }
            byte [] decodeResultBytes = new byte[allDecodeByteLen];
            for (int i = 0,curPosition = 0; i < encodePartLen; i++) {
                byte[] tempSorceBytes = decodeListData.get(i);
                int tempSourceBytesLen = tempSorceBytes.length;
                System.arraycopy(tempSorceBytes,0,decodeResultBytes,curPosition,tempSourceBytesLen);
                curPosition += tempSourceBytesLen;
            }
            decodeStrResult = new String(decodeResultBytes,"UTF-8");
        }catch (Exception e){
            e.printStackTrace();
        }
        return decodeStrResult;
    }
}
//運行結果
//加密后:bapvkf7RsocUqpr7JJ5yMDQSrMAKnWVuHG2buJDhV0DGMTlAKezcy7Qyc8f5DVVHralZ5I0nSZdQOVaIG7ndP/bvtNcQVJaaigRSaQTfmf7xiNNuWQf71nQJmiUlHdzijUWB0HbilWBeZ71FZT9djoV1X6EZDB3oH1DQwwOmvow=
//解密后:Java資料社區

經過以上加密之后,基本上能很大程度解決數據在傳輸當中數據被泄漏的幾率,但是并沒法保證絕對的安全,RSA加密中如果公鑰被泄漏,攻擊者照樣有辦法能夠根據泄漏的公鑰,重新模擬一份加密數據傳輸到服務端,而服務端并沒法辨別中途是否被篡改過,那么簽名技術就是用于驗證、防止中途是否被篡改;

 

簽名:通過使用兩對RSA秘鑰 (A[公鑰、私鑰],B[公鑰、私鑰]) ,通常客戶端存儲A公鑰、B私鑰,服務端則存儲A私鑰、B公鑰,A用于加解密,B用于簽名以及驗簽;

 

流程:客戶端用A公鑰加密過后,再通過B私鑰將加密過后的數據進行簽名,最后將加密數據與簽名一起發送到服務端,服務端接收到數據之后,首先通過B公鑰對該加密數據以及簽名進行對比驗簽,如果驗證不一致則代表中途被篡改過,否則正常;

 

RSA簽名案例:

  public class RSASignature {

    public static void main(String[] args) {
        /*創建兩份公私鑰,map1用于加解密、map2用于簽名*/
        Map<String,String> map1=RSAUtils.createRSAKeys();
        Map<String,String> map2=RSAUtils.createRSAKeys();

        String str="Java資料社區";
        String enStr=RSAUtils.encode(str,map1.get("public"));//map1公鑰加密
        System.out.println("加密后:"+enStr);
        String sign=sign(enStr,map2.get("private"));//map2私鑰簽名
        System.out.println("簽名:"+sign);
        //enStr+="1"; //模擬中途被篡改,則驗簽false
        boolean flag=doCheck(enStr,sign,map2.get("public"));//map2公鑰驗簽
        System.out.println("驗簽結果:"+flag);
    }
    /**
     * 簽名算法
     */
    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    /**
     * RSA簽名
     */
    public static String sign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes());
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * RSA驗簽
     */

    public static boolean doCheck(String content, String sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

            java.security.Signature signature = java.security.Signature
                    .getInstance(SIGN_ALGORITHMS);

            signature.initVerify(pubKey);
            signature.update(content.getBytes());

            boolean bverify = signature.verify(Base64.decode(sign));
            return bverify;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }
}
//運行結果:
//加密后:Nv1kTCukFAziwsCYPZQDQ8WqII1v5DKlTaFlcgkXQACuO01rFuvPAu/PXlmVuHN0i2Xx5B4ZjsKIs2YJHUzIunuqkPchKZM29b52jv7TLPaeZUeQFmC5GKEpEHSTWfUK9T7YF20+kK6Ey0rWRgOBd3ZoVPjvCoNXAlhyEYkBx7Q=
//簽名:fAkREdyQsioXeDi/CLaBQDP+V0K6W8DGXTBYZsH5GS8O30+ZKCfyTwvGNIZ++XIekt0P6xo1T6L7BRdT/it5qNwcqudxoolgf8KkhkSRFCI6LjJ6TYxFfCnvtMv7dxXDkR30E0AR9jyqNCUVE6ljUDsSL7PvUFpqOBDTcd+l2uA=
//驗簽結果:true

總結

1.對稱加密效率性能高,由于秘鑰存在泄漏的可能,并不能保證加密數據不被破解,建議對普通數據進行使用,對于敏感數據(金額、身份證等等)避免使用;

2.非對稱加密效率性能低,分為公私鑰兩把,即使公鑰泄漏,也能保證數據不被破解,建議對敏感數據進行使用;

3.通過非對稱加密+簽名能夠很大程度防止傳輸過程中信息被篡改,但是并不是絕對的安全;

 

關注微信公眾號"Java資料社區",更多干貨等你學習;

分享到:
標簽:Java
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定