知識點總結--太變態很長的文章

瓜瓜東西發表於2018-08-29

16 加解密和數字摘要RSA


    實際的例子是: post請求  類轉字串私鑰加密,簽名作為header, 服務端攔截器header攔截 如果header簽名通過驗證,則放行,介面裡解密,否則返回錯誤碼


    rest的數字簽名的意義 簡短的說,數字簽名 最好 私鑰加密公鑰解密,但是公鑰加密,私鑰解密裡的密碼 每次都不一樣
    https://blog.csdn.net/rnzuozuo/article/details/38357759        

其實公鑰和私鑰都可以用來加密或解密---只要能保證用A加密,就用B解密就行。至於A是公鑰還是私鑰,其實可以根據不同的用途而定。

例如說,如果你想把某個訊息祕密的發給某人,那你就可以用他的公鑰加密。因為只有他知道他的私鑰,所以這訊息也就只有他本人能解開,於是你就達到了你的目的。

但是如果你想釋出一個公告,需要一個手段來證明這確實是你本人發的,而不是其他人冒名頂替的。那你可以在你的公告開頭或者結尾附上一段用你的私鑰加密的內容(例如說就是你公告正文的一段話),那所有其他人都可以用你的公鑰來解密,看看解出來的內容是不是相符的。如果是的話,那就說明這公告確實是你發的---因為只有你的公鑰才能解開你的私鑰加密的內容,而其他人是拿不到你的私鑰的。

最後再說一下數字簽名
數字簽名無非就兩個目的:
證明這訊息是你發的;
證明這訊息內容確實是完整的---也就是沒有經過任何形式的篡改(包括替換、缺少、新增)。

其實,上面關於“公告”那段內容,已經證明了第一點:證明這訊息是你發的。
那麼要做到第二點,也很簡單,就是把你公告的原文做一次雜湊(md5或者sha1都行),然後用你的私鑰加密這段雜湊作為簽名,並一起公佈出去。當別人收到你的公告時,他可以用你的公鑰解密你的簽名,如果解密成功,並且解密出來的雜湊值確實和你的公告原文一致,那麼他就證明了兩點:這訊息確實是你發的,而且內容是完整的。

其實概念很簡單:

  1. 小明想祕密給小英傳送訊息

  2. 小英手裡有一個盒子(public key),這個盒子只有小英手裡的鑰匙(private key)才打得開

  3. 小英把盒子送給小明(分發公鑰)

  4. 小明寫好訊息放進盒子裡,鎖上盒子(公鑰加密

  5. 小明把盒子寄給小英(密文傳輸)

  6. 小英用手裡的鑰匙開啟盒子,得到小明的訊息(私鑰解密)

  7. 假設小剛劫持了盒子,因為沒有小英的鑰匙,他也打不開

        restful 例子

客戶端部分程式碼

 //建立客戶端簽名  
    String clientToken = new CreateSignTokenImpl().getToken(summary,secretKey);  
    HttpURLConnection urlConnection =  
    (HttpURLConnection) (new URL(hostURL)).openConnection();  
    urlConnection.setRequestProperty("Token", "jingdong "+accessKey+":"+clientToken);  
    urlConnection.setRequestProperty("Date", requestDate);  
    urlConnection.setDoInput(true);  
    urlConnection.setRequestMethod(httpMethod);  
服務端部分程式碼


    public Response downloadObject(@PathParam(value = "bucketName") String bucketName,@PathParam(value = "objectName") String objectName,@Context HttpServletRequest request)
    {
        BucketObject bucketObject = objectManager.downloadObject(request.getHeader(CommonConstant.USER_TOKEN),CommonsUtil.generateSummary(request), bucketName, objectName);
        //直接返回輸出流
        return Response.ok(new BigFileOutputStream(bucketObject.getDataStream())).build();

 

 

最完整的例子

package com.hbean.btlcloud.rsa;

import java.util.Map;

public class RSATester {

    static String publicKey;
    static String privateKey;

    static {
        try {
            Map<String, Object> keyMap = RSAUtils.genKeyPair();
            publicKey = RSAUtils.getPublicKey(keyMap);
            privateKey = RSAUtils.getPrivateKey(keyMap);
            System.err.println("公鑰: \n\r" + publicKey);
            System.err.println("私鑰: \n\r" + privateKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
//        test();
        testSign();
//        testHttpSign();
    }

    static void test() throws Exception {
        System.err.println("公鑰加密——私鑰解密");
        String source = "這是一行沒有任何意義的文字,你看完了等於沒看,不是嗎?";
//        String source ="S33DB/izk+OkIEjYpR7Fgw==";
        System.out.println("\r加密前文字:\r\n" + source);
        byte[] data = source.getBytes();

        System.out.println("public key"+publicKey);
        System.out.println("private key"+privateKey);
        publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChsMkb7M+fsTH3Oj5aI5NHdV6Y0UXJm8EVOPyr0nIIF1aCRl6NeEf2T602vlJnA0rbt8yGlHCjLkR3Vl2zzoDDcDpBBReuykFO82/TIE+HX/B5YY9IvkfvBJEZ7U5/BrZYHJm0quRhB0J7NMQtnvwIL+tt3gnCiv+amh/+KJUtHQIDAQAB";
        privateKey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKGwyRvsz5+xMfc6Plojk0d1XpjRRcmbwRU4/KvScggXVoJGXo14R/ZPrTa+UmcDStu3zIaUcKMuRHdWXbPOgMNwOkEFF67KQU7zb9MgT4df8Hlhj0i+R+8EkRntTn8GtlgcmbSq5GEHQns0xC2e/Agv623eCcKK/5qaH/4olS0dAgMBAAECgYBegdhqrBYHPyj3WmHLifOx1tZ9+AQUfSd0YiS9wXnJZOq5pqvpgJ5/R770unWrpG8C0gGHwAe+OXLnVeHbER6NL1ezDFbC18vjiTq8ZWIBN3kvy81ghdHmna5DwWYW/8LkwAYzX00pIAeYp5ou8eVilGsAXso0djLjBUbovmPqgQJBANVIX66CY39A0ce0vWt5YxNhYBrOVRwZKkxffDwxqLbc4UcCjlvXnvcxPP2z0YbmuygfDZSYfQRZOENJB0dAp+0CQQDCEyHh88jBolRewrS4tcB4tFCQrGtU9tQeuArJrjQkgQtaDhW2I32pJNixIWXWX5zrsTzeRQWG0I8q+QjIMpPxAkBZE2f6rzQ00nKK0J6Hw2OwlWuY5nG/UXdL/FkbnrXS4X8otBWWoAAhRyws3Sv2BmlGAWoflGwqI8UOvPJQPN2lAkEAvIEo3/eSf8HUgVZNgzvGf3dk6aEPf69fyZapo8t5ChvtksMrg4Zlf+0yLJpr+BVWrfexT5NrEs9wwCSzywCVgQJAHRk+NCJVd5I9RAsecAVTIgi5f2IeE+rvdJ/JdT3Ae4G45Wjo+751V6oiTUZ1iVE2bsRZCZ9wRgP/Y5wcHVLWug==";
        byte[] encodedData = RSAUtils.encryptByPublicKey(data, publicKey);
        System.out.println("加密後文字:\r\n" + new String(encodedData));
        byte[] decodedData = RSAUtils.decryptByPrivateKey(encodedData, privateKey);
        
//        byte[] encodedData = RSAUtils.encryptByPrivateKey(data, privateKey);
//        System.out.println("加密後文字:\r\n" + new String(encodedData));
//        byte[] decodedData = RSAUtils.decryptByPublicKey(encodedData, publicKey);
        
        
        String target = new String(decodedData);
        System.out.println("解密後文字: \r\n" + target);
    }

    static void testSign() throws Exception {
        System.err.println("私鑰加密——公鑰解密");
        String source = "這是一行測試RSA數字簽名的無意義文字";
        System.out.println("原文字:\r\n" + source);
        byte[] data = source.getBytes();
        byte[] encodedData = RSAUtils.encryptByPrivateKey(data, privateKey);
        System.out.println("加密後:\r\n" + new String(encodedData));
        byte[] decodedData = RSAUtils.decryptByPublicKey(encodedData, publicKey);
        String target = new String(decodedData);
        System.out.println("解密後: \r\n" + target);
        System.err.println("私鑰簽名——公鑰驗證簽名");
        String sign = RSAUtils.sign(encodedData, privateKey);
        System.err.println("簽名:\r" + sign);
        boolean status = RSAUtils.verify(encodedData, publicKey, sign);
        System.err.println("驗證結果:\r" + status);
    }
    
    static void testHttpSign() throws Exception {
        String param = "id=1&name=張三";
        byte[] encodedData = RSAUtils.encryptByPrivateKey(param.getBytes(), privateKey);
        System.out.println("加密後:" + encodedData);
        
        byte[] decodedData = RSAUtils.decryptByPublicKey(encodedData, publicKey);
        System.out.println("解密後:" + new String(decodedData));
        
        String sign = RSAUtils.sign(encodedData, privateKey);
        System.err.println("簽名:" + sign);
        
        boolean status = RSAUtils.verify(encodedData, publicKey, sign);
        System.err.println("簽名驗證結果:" + status);
    }
}

 

package com.hbean.btlcloud.rsa;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
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 org.apache.shiro.codec.Base64;

/**
 * <p>
 * RSA公鑰/私鑰/簽名工具包
 * </p>
 * <p>
 * 羅納德·李維斯特(Ron [R]ivest)、阿迪·薩莫爾(Adi [S]hamir)和倫納德·阿德曼(Leonard [A]dleman)
 * </p>
 * <p>
 * 字串格式的金鑰在未在特殊說明情況下都為BASE64編碼格式<br/>
 * 由於非對稱加密速度極其緩慢,一般檔案不使用它來加密而是使用對稱加密,<br/>
 * 非對稱加密演算法可以用來對對稱加密的金鑰加密,這樣保證金鑰的安全也就保證了資料的安全
 * </p>
 * 
 * @author IceWee
 * @date 2012-4-26
 * @version 1.0
 */
 class RSAUtils {

    /**
     * 加密演算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    
    /**
     * 簽名演算法
     */
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    /**
     * 獲取公鑰的key
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";
    
    /**
     * 獲取私鑰的key
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * <p>
     * 生成金鑰對(公鑰和私鑰)
     * </p>
     * 
     * @return
     * @throws Exception
     */
    public static Map<String, Object> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }
    
    /**
     * <p>
     * 用私鑰對資訊生成數字簽名
     * </p>
     * 
     * @param data 已加密資料
     * @param privateKey 私鑰(BASE64編碼)
     * 
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return Base64Utils.encode(signature.sign());
    }

    /**
     * <p>
     * 校驗數字簽名
     * </p>
     * 
     * @param data 已加密資料
     * @param publicKey 公鑰(BASE64編碼)
     * @param sign 數字簽名
     * 
     * @return
     * @throws Exception
     * 
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64Utils.decode(sign));
    }

    /**
     * <P>
     * 私鑰解密
     * </p>
     * 
     * @param encryptedData 已加密資料
     * @param privateKey 私鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 對資料分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * <p>
     * 公鑰解密
     * </p>
     * 
     * @param encryptedData 已加密資料
     * @param publicKey 公鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 對資料分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * <p>
     * 公鑰加密
     * </p>
     * 
     * @param data 源資料
     * @param publicKey 公鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 對資料加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 對資料分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * <p>
     * 私鑰加密
     * </p>
     * 
     * @param data 源資料
     * @param privateKey 私鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 對資料分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * <p>
     * 獲取私鑰
     * </p>
     * 
     * @param keyMap 金鑰對
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64Utils.encode(key.getEncoded());
    }

    /**
     * <p>
     * 獲取公鑰
     * </p>
     * 
     * @param keyMap 金鑰對
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64Utils.encode(key.getEncoded());
    }

}

public class Base64Utils {

    /**
     * 檔案讀取緩衝區大小
     */
    private static final int CACHE_SIZE = 1024;

    /**
     * <p>
     * BASE64字串解碼為二進位制資料
     * </p>
     * 
     * @param base64
     * @return
     * @throws Exception
     */
    public static byte[] decode(String base64) throws Exception {
        return Base64.decode(base64.getBytes());
    }

    /**
     * <p>
     * 二進位制資料編碼為BASE64字串
     * </p>
     * 
     * @param bytes
     * @return
     * @throws Exception
     */
    public static String encode(byte[] bytes) throws Exception {
        return new String(Base64.encode(bytes));
    }

    /**
     * <p>
     * 將檔案編碼為BASE64字串
     * </p>
     * <p>
     * 大檔案慎用,可能會導致記憶體溢位
     * </p>
     * 
     * @param filePath
     *            檔案絕對路徑
     * @return
     * @throws Exception
     */
    public static String encodeFile(String filePath) throws Exception {
        byte[] bytes = fileToByte(filePath);
        return encode(bytes);
    }

    /**
     * <p>
     * BASE64字串轉回檔案
     * </p>
     * 
     * @param filePath
     *            檔案絕對路徑
     * @param base64
     *            編碼字串
     * @throws Exception
     */
    public static void decodeToFile(String filePath, String base64) throws Exception {
        byte[] bytes = decode(base64);
        byteArrayToFile(bytes, filePath);
    }

    /**
     * <p>
     * 檔案轉換為二進位制陣列
     * </p>
     * 
     * @param filePath
     *            檔案路徑
     * @return
     * @throws Exception
     */
    public static byte[] fileToByte(String filePath) throws Exception {
        byte[] data = new byte[0];
        File file = new File(filePath);
        if (file.exists()) {
            FileInputStream in = new FileInputStream(file);
            ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
            byte[] cache = new byte[CACHE_SIZE];
            int nRead = 0;
            while ((nRead = in.read(cache)) != -1) {
                out.write(cache, 0, nRead);
                out.flush();
            }
            out.close();
            in.close();
            data = out.toByteArray();
        }
        return data;
    }

    /**
     * <p>
     * 二進位制資料寫檔案
     * </p>
     * 
     * @param bytes
     *            二進位制資料
     * @param filePath
     *            檔案生成目錄
     */
    public static void byteArrayToFile(byte[] bytes, String filePath) throws Exception {
        InputStream in = new ByteArrayInputStream(bytes);
        File destFile = new File(filePath);
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }
        destFile.createNewFile();
        OutputStream out = new FileOutputStream(destFile);
        byte[] cache = new byte[CACHE_SIZE];
        int nRead = 0;
        while ((nRead = in.read(cache)) != -1) {
            out.write(cache, 0, nRead);
            out.flush();
        }
        out.close();
        in.close();
    }
}



 

相關文章