非对称加密算法RSA

214 阅读4分钟

使用对称密码时一定会遇到密钥配送问题,解决密钥配送问题的方法有以下几种:

  1. 通过事先共享密钥来解决:有时候现实无法满足
  2. 通过密钥分配中心来解决:密钥分配中心的安全和负载都是潜在危险因素
  3. 通过Diffie-Hellman密钥交换算法来解决:示例
  4. 通过公钥密钥来解决

1976年Whitfield Diffie和Martin Hellman提出了关于公钥密码设计的思想,尽管他们没有提出具体的公钥密码算法,但他们提出了应该将加密密钥和解密密钥分开的想法,而且还描述了公钥密码应该具备的性质。

1977年Ralph Merkle和Martin Hellman共同设计了一种具体的公钥密码算法--- Knapsack。该算法申请了专利,但后来被发现并不安全。

1978年美国MIT的Ron Rivest、Adi Shamir和Leonard Adleman共同发表了一种公钥密码算法---RSA(三人姓氏首字母缩写)。RSA可以说是现在公钥密码的事实标准

公钥密码也叫非对称密码,密钥分为加密密钥和解密密钥。发送者用加密密钥对消息进行加密,接收者用解密密钥对密文进行解密。也就是说解密密钥从一开始就是由接收者自己保管的,因此只要将加密密钥发给发送者就可以解决密钥配送问题了,而根本不需要配送解密密钥。

非对称密码的加密密钥一般称为公钥,解密密钥一般称为私钥,公钥和私钥合称密钥对。公钥是公开的,可以通过不安全的网络传输;私钥则是保密的。公钥加密,私钥解密。当Alice想与Bob安全通信时,Alice先生成密钥对,接着将公钥发送给Bob,Bob用拿到的公钥加密要通信的内容然后将密文发送给Alice,Alice则用手中的私钥进行解密。

在非对称加密算法中,几乎所有的算法都是基于数学问题而建立的。RSA算法基于大数因子分解数学难题,其他的非对称加密算法如Rabin利用了mod N下求平方根的困难,EIGamal算法和ECC算法(椭圆曲线密码,Elliptic Curve Cryptography)基于离散对数难题。

RSA算法的公钥长度<<私钥长度。公钥密码加密速度比对称密码加密速度慢大约几百倍。

package com.fulcrum.encrypt;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSADemo {

    private static final String RSA_ALGORITHM = "RSA";
    private static final String PUBLIC_KEY = "PUBLIC_KEY";
    private static final String PRIVATE_KEY = "PRIVATE_KEY";

    public static void main(String[] args) throws Exception{
        Map<String, byte[]> skMap = initKey();
        byte[] bytesPublicKey = skMap.get(PUBLIC_KEY);
        byte[] bytesPrivateKey = skMap.get(PRIVATE_KEY);
        String str = "滑坡谬误的逻辑错误";
        System.out.println("原文:"+str);
        byte[] cipherText = encryptByPublicKey(str.getBytes(StandardCharsets.UTF_8), bytesPublicKey);
        System.out.println("密文:"+Base64.encodeBase64String(cipherText));
        System.out.println("解密:"+decryptByPrivateKey(cipherText, bytesPrivateKey));
    }

    public static String decryptByPrivateKey(byte[] data, byte[] bytesPrivateKey) throws Exception{
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(bytesPrivateKey);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(data));
    }

    public static byte[] encryptByPublicKey(byte[] data, byte[] bytesPublicKey) throws Exception{
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(bytesPublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    public static Map<String, byte[]> initKey() throws Exception{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        byte[] bytesPublicKey = keyPair.getPublic().getEncoded();
        byte[] bytesPrivateKey = keyPair.getPrivate().getEncoded();
        System.out.println("公钥字节数:"+bytesPublicKey.length);
        System.out.println("私钥字节数:"+bytesPrivateKey.length);
        Map<String, byte[]> skMap = new HashMap<>();
        skMap.put(PUBLIC_KEY, bytesPublicKey);
        skMap.put(PRIVATE_KEY, bytesPrivateKey);
        return skMap;
    }
}

截屏2024-03-14 17.19.57.png

公钥密码解决了密钥配送问题,但无法判断得到的公钥是否正确合法,因为它解决不了中间人攻击问题:

当Alice与Bob利用公钥密码进行通信时,Bob生成一对密钥并将公钥发送给Alice,Alice拿到公钥然后加密信息,接着将加密后的信息发送给Bob,Bob利用自己的私钥解密得到明文。看起来一切都很美好,但是这一旦遭遇中间人攻击,通信就完全失效了。

Bob生成一对密钥并将公钥发送给Alice,这时网络中的一个叫Hacker的中间人截获了这个公钥并保存下来,接着Hacker自己生成一对密钥并将其中的公钥发送给Alice,Alice拿到公钥利用公钥加密信息然后发送给Bob,此时Hacker再次截获信息并利用自己的私钥解密了信息。Bob可以修改这个信息或者干脆重新生成一个替代信息,然后利用截获到的Bob发送的公钥加密信息并发送给Bob,Bob利用自己的私钥解密得到明文,于是Alice和Bob的通信内容完全被篡改了但是两人却浑然不知,这就是中间人攻击。仅靠公钥密码是无法防御中间人攻击的。

我们好不容易用公钥密码解决了密钥配送问题,又遇到了中间人攻击,真是一波未平一波又起。要防御中间人攻击,还需要一种手段来确认所收到的公钥确实属于Bob,这种手段称为认证。这种情况下,我们使用公钥的证书来解决。