写点什么

Java 加密技术 (四) 非对称加密算法 RSA

  • 2022 年 9 月 06 日
    上海
  • 本文字数:7100 字

    阅读完需:约 23 分钟

Java加密技术(四)非对称加密算法RSA

​一、RSA 基本概念

    这种算法 1978 年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和 Leonard Adleman。 

    这种加密算法的特点主要是密钥的变化,上文我们看到 DES 只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。RSA 同时有两把钥匙,公钥与私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。 

     非对称加密算法是一种密钥的保密方法。

     非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对加密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。

    另一方面,甲方可以使用乙方的公钥对加密信息进行签名后再发送给乙方;乙方再用自己的私匙对数据进行验签。

甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。 非对称加密算法的保密性比较好,它消除了最终用户交换密钥的需要。

二、非对称密码体制的特点

算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多。

三、工作原理

  1. A 要向 B 发送信息,A 和 B 都要产生一对用于加密和解密的公钥和私钥。

  2. A 的私钥保密,A 的公钥告诉 B;B 的私钥保密,B 的公钥告诉 A。

  3. A 要给 B 发送信息时,A 用 B 的公钥加密信息,因为 A 知道 B 的公钥。

  4. A 将这个消息发给 B(已经用 B 的公钥加密消息)。

  5. B 收到这个消息后,B 用自己的私钥解密 A 的消息。其他所有收到这个报文的人都无法解密,因为只有 B 才有 B 的私钥。

四、流程分析

1. 甲方构建密钥对儿,将公钥公布给乙方,将私钥保留。

2. 甲方使用私钥加密数据,然后用私钥对加密后的数据签名,发送给乙方签名以及加密后的数据;乙方使用公钥、签名来验证待解密数据是否有效,如果有效使用公钥对数据解密。

3. 乙方使用公钥加密数据,向甲方发送经过加密后的数据;甲方获得加密数据,通过私钥解密。

按如上步骤给出序列图,如下: 

 



五、Demo

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;
/** 
 * RSA安全编码组件 
 * @author Sunny 
 * @version 1.0 
 * @since 1.0 
 */  
public abstract class RSACoder extends Coder {  
    public static final String KEY_ALGORITHM = "RSA";  
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";  
    private static final String PUBLIC_KEY = "RSAPublicKey";  
    private static final String PRIVATE_KEY = "RSAPrivateKey";  
    /** 
     * 用私钥对信息生成数字签名
     * @param data 
     * 加密数据 
     * @param privateKey 
     * 私钥
     * @return 
     * @throws Exception 
     */  
    public static String sign(byte[] data, String privateKey) throws Exception {  
        // 解密由base64编码的私钥        byte[] keyBytes = decryptBASE64(privateKey);
        // 构造PKCS8EncodedKeySpec对象        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        // KEY_ALGORITHM 指定的加密算法        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取私钥匙对象        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
        // 用私钥对信息生成数字签名        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initSign(priKey);  
        signature.update(data);  
        return encryptBASE64(signature.sign());  
    }  
  
    /** 
     * 校验数字签名
     * @param data 
     * 加密数据 
     * @param publicKey 
     * 公钥 
     * @param sign 
     * 数字签名 
     *  
     * @return 校验成功返回true 失败返回false 
     * @throws Exception
     */  
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {  
        // 解密由base64编码的公钥        byte[] keyBytes = decryptBASE64(publicKey);
        // 构造X509EncodedKeySpec对象        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
        // KEY_ALGORITHM 指定的加密算法        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        // 取公钥匙对象        PublicKey pubKey = keyFactory.generatePublic(keySpec);  
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initVerify(pubKey);  
        signature.update(data);          // 验证签名是否正常        return signature.verify(decryptBASE64(sign));  
    }
    /** 
     * 解密
     * 用私钥解密
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {  
        // 对密钥解密        byte[] keyBytes = decryptBASE64(key);
        // 取得私钥        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 对数据解密        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);           return cipher.doFinal(data);  
    }
    /** 
     * 解密<br> 
     * 用公钥解密
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {  
        // 对密钥解密        byte[] keyBytes = decryptBASE64(key);
        // 取得公钥        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);  
        // 对数据解密        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);  
    }  
    /** 
     * 加密
     * 用公钥加密
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {  
        // 对公钥解密        byte[] keyBytes = decryptBASE64(key);
        // 取得公钥        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
        return cipher.doFinal(data);  
    }  
    /** 
     * 加密
     * 用私钥加密 
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {  
        // 对密钥解密        byte[] keyBytes = decryptBASE64(key);
        // 取得私钥        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 对数据加密        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
        return cipher.doFinal(data);  
    }    
    /** 
     * 取得私钥 
     * @param keyMap 
     * @return 
     * @throws Exception 
     */  
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {  
        Key key = (Key) keyMap.get(PRIVATE_KEY);  
        return encryptBASE64(key.getEncoded());  
    }
    /** 
     * 取得公钥
     * @param keyMap 
     * @return 
     * @throws Exception 
     */  
    public static String getPublicKey(Map<String, Object> keyMap)  throws Exception {  
        Key key = (Key) keyMap.get(PUBLIC_KEY);  
        return encryptBASE64(key.getEncoded());  
    }
    /** 
     * 初始化密钥     * @return 
     * @throws Exception 
     */  
    public static Map<String, Object> initKey() 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;  
    }  
}  
复制代码

 

再给出一个测试类: 

import static org.junit.Assert.*;import org.junit.Before;import org.junit.Test;import java.util.Map;
/** 
 *  
 * @author Sunny 
 * @version 1.0 
 * @since 1.0 
 */  
public class RSACoderTest {  
    private String publicKey;  
    private String privateKey;  
    @Before    public void setUp() throws Exception {  
        Map<String, Object> keyMap = RSACoder.initKey();  
        publicKey = RSACoder.getPublicKey(keyMap);  
        privateKey = RSACoder.getPrivateKey(keyMap);  
        System.err.println("公钥: \n\r" + publicKey);  
        System.err.println("私钥: \n\r" + privateKey);  
    }
    @Test    public void test() throws Exception {  
        System.err.println("公钥加密——私钥解密");  
        String inputStr = "abc";  
        byte[] data = inputStr.getBytes();  
        byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);
        byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData, privateKey);         String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);
    }
    @Test  
    public void testSign() throws Exception {  
        System.err.println("私钥加密——公钥解密");  
        String inputStr = "sign";  
        byte[] data = inputStr.getBytes();
        byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);
        byte[] decodedData = RSACoder.decryptByPublicKey(encodedData, publicKey);  
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);
        System.err.println("私钥签名——公钥验证签名");  
        // 产生签名        String sign = RSACoder.sign(encodedData, privateKey);  
        System.err.println("签名:\r" + sign);         // 验证签名        boolean status = RSACoder.verify(encodedData, publicKey, sign);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
    }  
复制代码

六、总结

    简要总结一下,使用公钥加密、私钥解密,完成了乙方到甲方的一次数据传递,通过私钥加密、公钥解密,同时通过私钥签名、公钥验证签名,完成了一次甲方到乙方的数据传递与验证,两次数据传递完成一整套的数据交互! 

类似数字签名,数字信封是这样描述的

数字信封 

  数字信封用加密技术来保证只有特定的收信人才能阅读信的内容。 

流程: 

    信息发送方采用对称密钥来加密信息,然后再用接收方的公钥来加密此对称密钥(这部分称为数字信封),再将它和信息一起发送给接收方;接收方先用相应的私钥打开数字信封,得到对称密钥,然后使用对称密钥再解开信息。 

发布于: 刚刚阅读数: 3
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
Java加密技术(四)非对称加密算法RSA_RSA密码_No Silver Bullet_InfoQ写作社区