加密与安全_探索非对称加密算法_RSA算法

2024-05-26 13:17:19 浏览数 (3)

Pre

加密与安全_探索密钥交换算法(Diffie-Hellman算法) 中我们可以看到,公钥-私钥组成的密钥对是非常有用的加密方式,因为公钥是可以公开的,而私钥是完全保密的,由此奠定了非对称加密的基础。

非对称加密确实是一种非常有用的加密方式,其基础就是公钥和私钥组成的密钥对。公钥可以公开,而私钥则是完全保密的,这种特性为非对称加密提供了很好的安全性。

在非对称加密中,加密和解密使用的不是相同的密钥。只有同一个密钥对中的公钥和私钥才能正常地进行加密和解密操作

因此,如果小明要向小红发送加密文件,他应该首先向小红索取她的公钥。然后,小明使用小红的公钥对文件进行加密,并将加密后的文件发送给小红。由于只有小红持有与公钥对应的私钥,因此只有小红能够使用她的私钥解密这个文件。其他人无法使用公钥解密文件,因为只有私钥的持有者才能解密数据。

这种方式确保了文件在传输过程中的安全性,只有具备私钥的接收方才能解密文件,保护了通信的机密性。

主流的非对称加密算法

主流的非对称加密算法包括:

  1. RSA(Rivest-Shamir-Adleman): RSA是最常用的非对称加密算法之一,它基于大数分解的数学难题。RSA算法可以用于加密、数字签名和密钥交换等场景,广泛应用于网络通信、数据传输和身份认证等领域。
  2. DSA(Digital Signature Algorithm): DSA是一种数字签名算法,专门用于生成和验证数字签名,常用于身份认证、数据完整性验证等场景。DSA算法基于离散对数的数学难题,相对于RSA算法,它的加密和解密速度更快。
  3. ECC(Elliptic Curve Cryptography): ECC是一种基于椭圆曲线的非对称加密算法,具有与RSA相当的安全性,但在密钥长度较短的情况下提供了更高的安全性,因此在资源受限的环境下更加适用。
  4. ElGamal: ElGamal是一种基于离散对数的非对称加密算法,主要用于密钥交换和加密通信。与RSA类似,ElGamal算法也可以用于加密和数字签名,但相对于RSA,ElGamal算法在实现和使用上更为复杂。

这些非对称加密算法在不同的场景下有着各自的优缺点和适用性,选择合适的算法取决于具体的安全需求、性能要求和应用环境。

典型算法:RSA

非对称加密的典型算法就是RSA算法,它是由Ron Rivest、Adi Shamir和Leonard Adleman这三位密码学家共同发明的,因此用他们三人的姓的首字母缩写表示。

相比对称加密,非对称加密有显著的优点。对称加密需要在通信双方之间协商共享密钥,而非对称加密则可以安全地公开各自的公钥。

  • 在N个人之间进行通信时,使用非对称加密只需要N个密钥对,每个人只需管理自己的密钥对。
  • 而使用对称加密时,则需要N*(N-1)/2个密钥,因此每个人需要管理N-1个密钥,密钥管理的难度大,并且容易导致密钥泄漏。

非对称加密的缺点就是运算速度非常慢,比对称加密要慢很多。 在实际应用中,通常会将非对称加密和对称加密结合使用,以充分发挥它们各自的优点,并弥补彼此的缺点。

假设小明需要向小红传输加密文件,他们可以采取以下步骤:

  1. 小明和小红首先交换各自的公钥,这可以在安全的通信渠道上完成。
  2. 小明生成一个随机的对称密钥(比如AES口令),然后使用小红的公钥通过RSA算法对这个对称密钥进行加密,得到密文。
  3. 小明将加密后的对称密钥发送给小红。
  4. 小红使用自己的RSA私钥对收到的密文进行解密,得到原始的对称密钥。
  5. 现在,小明和小红都拥有相同的对称密钥,他们可以使用对称加密算法(如AES)来加密和解密通信内容。由于对称加密算法的运算速度快,因此通信双方可以更高效地进行加密通信。

通过这种方式,非对称加密用于安全地传输对称密钥,而对称加密用于加密和解密实际的通信内容,既保证了安全性,又提高了效率。

Code

代码语言:javascript复制
package com.artisan.securityalgjava.rsa;

import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class RSAExample {

    public static void main(String[] args) throws Exception {
        // 生成RSA密钥对
        KeyPair keyPair = generateRSAKeyPair();

        // 获取公钥和私钥
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // 要加密的原始数据
        String plaintext = "Hello, RSA!";
        System.out.println("Plaintext: "   plaintext);

        // 使用公钥加密数据
        byte[] encryptedBytes = encryptRSA(plaintext.getBytes(), publicKey);
        String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
        System.out.println("Encrypted text: "   encryptedText);

        // 使用私钥解密数据
        byte[] decryptedBytes = decryptRSA(Base64.getDecoder().decode(encryptedText), privateKey);
        String decryptedText = new String(decryptedBytes);
        System.out.println("Decrypted text: "   decryptedText);
    }

    // 生成RSA密钥对
    public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048); // 设置密钥长度为2048位
        return keyPairGenerator.generateKeyPair();
    }

    // 使用公钥加密数据
    public static byte[] encryptRSA(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    // 使用私钥解密数据
    public static byte[] decryptRSA(byte[] encryptedData, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(encryptedData);
    }
}

输出:

代码语言:javascript复制
Plaintext: Hello, RSA!
Encrypted text: CU 9ZlNmH3NR5SZSdpZRQmTyXQLU0SLX1d1ICoi/LjAu6a4lMsVXUfB STFHglKOTVCEjddCJwoWVGXJDcm/nTqRVtB/qIV3 kt7W4/H2fvLeYf mgbIyNNMyZU4h7k0rbVSTIdHpmfcV1ToYPNVtYarHQ0EbmD261iKeGBGb9HrprknspsXxfIMAEeKOEFotE/4fkwySCsWatANwOwjivEqZcDBnX/1BlhA9CIP4zd4osUoiRt/wJtBsx58/A 47Lf2wBo7C8YcRRpa2A8HxtxnOkhy0cciVeaAzMtaaiVDWbaBqML3sXu3iP3CDkrZQ2 VmBCTZX35y1fxCZ1/Dg==
Decrypted text: Hello, RSA!

代码语言:javascript复制
package com.artisan.securityalgjava.rsa;

import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class RsaDemo {

    public static void main(String[] args) throws Exception {
        // 明文:
        byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8");
        // 创建公钥/私钥对
        Person alice = new Person("Alice");
        // 用Alice的公钥加密:
        byte[] pk = alice.getPublicKey();
        System.out.println(String.format("public key: %x", new BigInteger(1, pk)));
        byte[] encrypted = alice.encrypt(plain);
        System.out.println(String.format("encrypted: %x", new BigInteger(1, encrypted)));

        // 用Alice的私钥解密:
        byte[] sk = alice.getPrivateKey();
        System.out.println(String.format("private key: %x", new BigInteger(1, sk)));
        byte[] decrypted = alice.decrypt(encrypted);
        System.out.println(new String(decrypted, "UTF-8"));
    }
}

class Person {
    String name;
    // 私钥:
    PrivateKey sk;
    // 公钥:
    PublicKey pk;

    public Person(String name) throws GeneralSecurityException {
        this.name = name;
        // 生成公钥/私钥对:
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        kpGen.initialize(1024);
        KeyPair kp = kpGen.generateKeyPair();
        this.sk = kp.getPrivate();
        this.pk = kp.getPublic();
    }

    /**
     * 把私钥导出为字节
     * @return
     */
    public byte[] getPrivateKey() {
        return this.sk.getEncoded();
    }

    /**
     *  把公钥导出为字节
     * @return
     */
    public byte[] getPublicKey() {
        return this.pk.getEncoded();
    }

    /**
     * 用公钥加密
     * @param message
     * @return
     * @throws GeneralSecurityException
     */
    public byte[] encrypt(byte[] message) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, this.pk);
        return cipher.doFinal(message);
    }

    /**
     * 用私钥解密
     * @param input
     * @return
     * @throws GeneralSecurityException
     */
    public byte[] decrypt(byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, this.sk);
        return cipher.doFinal(input);
    }
}

输出:

代码语言:javascript复制
public key: 30819f300d06092a864886f70d010101050003818d00308189028181008c97547fab56c9af0f54d814581a7d695e0722c7029751e4d05530d28923c45ac29420e8cec253a71043e98dae2139cee1d97e125f73c0b32a91196a7154c3199cf6529942627e50796463fc7ab6c747ab0788fe67d3855c9f9348233277ef68d663c2df1232c5adb6d7d9be0aefe5e6c0bcdda48abf9570d732878b93c5bc6d0203010001
encrypted: 24bac571932a8f2c633660493a57e895e404c03824ba8fff87bfaaa655914eaeabd032beed102d24479c21e1c17ccfea5018708da42e28ec7e2f472b36f769f0aa0639bdff985babdbce3a9d357a89dcf31df2b00366f16708245c2a75a3c5d25aedd25207f3204416a6e431f305bd49b90eb0d40d6caed2bb4f068c183fb442
private key: 30820275020100300d06092a864886f70d01010105000482025f3082025b020100028181008c97547fab56c9af0f54d814581a7d695e0722c7029751e4d05530d28923c45ac29420e8cec253a71043e98dae2139cee1d97e125f73c0b32a91196a7154c3199cf6529942627e50796463fc7ab6c747ab0788fe67d3855c9f9348233277ef68d663c2df1232c5adb6d7d9be0aefe5e6c0bcdda48abf9570d732878b93c5bc6d02030100010281806b2753f1d2875d449decce9c02e27dbf7738fd1aad30e3ebff954e96c88b88369ca305ca2afc1581f975a966a0d716164630dc53e88872d09b9ae7c2270ab17f80e36bc78532d68216ecb3a62bc4ab84be8b6db08c48568622f2216e29609f3ae6db825c13d503554a923fe62850cd6fcf2221315c98946c3fd47c79f6bc5089024100fcc240d520a5106544a5cc764c9149c35fca005b9af0fcb5999fa5e4b69a1a5ca19f732e5a2633c0ceeceb7cc433e39f5fca0c7c9a1679ff9990029170adfe070241008e64db6f5b7270a69d4082c88db9c8c9909519b535bd8bf898f2fc6f3d7ac00c1fa8d2fa4614cb851f64f7bea021d60e5d27fe5f5aa0c21e0f05ef5f54e314eb0240460aca8e850258ddc73d2ec0a58d2964b3b9b589ad1114e67a10cc96e9a720a104c4bbd55f73f0a9806e14ffb91b2bfbb13ebb61180e1c76a126501fdf9ac7a7024020845bb0045c0fe99c837cda3bb32f6d083d644f836433b0a38ce9a4a58f8087c43b1362dfda23d7d4a18409de1b9bfc4fbdb0532a2907eb415703a0eb8ba7dd02401fee6a8ef7af9c5c723215500003984e5ded1bd31061ce5396bf5b55ab7cdbe8b6024863fd466eb8de13318863f485af479cc66b8d8a858f02b9b3254b0c1562
Hello, encrypt use RSA

RSA的公钥和私钥的恢复

RSA的公钥和私钥都可以通过getEncoded()方法获得以byte[]表示的二进制数据,并根据需要保存到文件中。

伪代码如下:

代码语言:javascript复制
byte[] pkData = ...
byte[] skData = ...
KeyFactory kf = KeyFactory.getInstance("RSA");
// 恢复公钥:
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
PublicKey pk = kf.generatePublic(pkSpec);
// 恢复私钥:
PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
PrivateKey sk = kf.generatePrivate(skSpec);

完整代码:

代码语言:javascript复制
package com.artisan.securityalgjava.rsa;

import java.security.*;
import java.security.spec.*;
import java.util.Base64;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */

public class RSAKeyConversionExample {

    public static void main(String[] args) throws Exception {
        // 生成RSA密钥对
        KeyPair keyPair = generateRSAKeyPair();

        // 获取公钥和私钥
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // 将公钥和私钥转换为byte[]数组
        byte[] publicKeyBytes = publicKey.getEncoded();
        byte[] privateKeyBytes = privateKey.getEncoded();

        // 将byte[]数组转换为Base64编码的字符串方便保存和传输
        String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKeyBytes);
        String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKeyBytes);

        System.out.println("Public key (Base64): "   publicKeyBase64);
        System.out.println("Private key (Base64): "   privateKeyBase64);

        // 从Base64编码的字符串恢复公钥和私钥
        PublicKey restoredPublicKey = restorePublicKey(Base64.getDecoder().decode(publicKeyBase64));
        PrivateKey restoredPrivateKey = restorePrivateKey(Base64.getDecoder().decode(privateKeyBase64));

        // 验证恢复的公钥和私钥与原始的是否一致
        System.out.println("Restored public key equals original: "   publicKey.equals(restoredPublicKey));
        System.out.println("Restored private key equals original: "   privateKey.equals(restoredPrivateKey));
    }

    // 生成RSA密钥对
    public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048); // 设置密钥长度为2048位
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 从byte[]数组恢复公钥
     * @param publicKeyBytes
     * @return
     * @throws Exception
     */
    public static PublicKey restorePublicKey(byte[] publicKeyBytes) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
        return keyFactory.generatePublic(publicKeySpec);
    }

    /**
     * 从byte[]数组恢复私钥
     * @param privateKeyBytes
     * @return
     * @throws Exception
     */
    public static PrivateKey restorePrivateKey(byte[] privateKeyBytes) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        return keyFactory.generatePrivate(privateKeySpec);
    }
}

输出

代码语言:javascript复制
Public key (Base64): MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd4mRIUmxWx9 Jrp/Gp06gqRbgsGdy36Dshvrkz5ofiKe6guRfP/swimmsdZItPpfWJNn2BKQzTL32gSiZ5y uU7EpkEDmg2Z6ip5jb0Dpt0/cTldD7ykG6AMlnpJwoFQXgNTCpgqOOhFRNRPTBLUjeVMeNeJPoVQKipdFTVrUU5NCZSHgvBWjngELmLZDdj1sJ/vCyEol1eg9N1G8c9kcyhbAhSGX7BSq3q/heuOPGjzHVTMJb2mKvGPst7fYdmUyVvW ovF7AMf8AfemLrdZJ mxi4dj2X5c2fKcNK32 zfJNhpQMdaLZtbHAAyvSVc6xGxiYYGAuUrFnGiY26XwIDAQAB
Private key (Base64): MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCl3iZEhSbFbH34mun8anTqCpFuCwZ3LfoOyG uTPmh Ip7qC5F8/ zCKaax1ki0 l9Yk2fYEpDNMvfaBKJnnL65TsSmQQOaDZnqKnmNvQOm3T9xOV0PvKQboAyWeknCgVBeA1MKmCo46EVE1E9MEtSN5Ux414k hVAqKl0VNWtRTk0JlIeC8FaOeAQuYtkN2PWwn 8LISiXV6D03Ubxz2RzKFsCFIZfsFKrer F6448aPMdVMwlvaYq8Y y3t9h2ZTJW9b6i8XsAx/wB96Yut1kn6bGLh2PZflzZ8pw0rfb7N8k2GlAx1otm1scADK9JVzrEbGJhgYC5SsWcaJjbpfAgMBAAECggEAMndY5Vgt57uOyGk58Bbj46G7hePM384ZWw4ZDMtW2LUqTV1qVtZaYjqrlkQ5FqOrUv7p5ygA8FnL/flISp7vFO9R/eKYnVmP1BI5P1ZRA3DBM8UIm0nbu54jWy6IBdzCpJzGTvpF1p0LkcIC4b8j66wFtNwc9NsyRC4NANwe90ytm6/HrsoUlRgMeJv9QejfA8A3 VrF1Y1ozn7aSuElX6q7tdZ0kNEGN7qVdl1qilmcMiPpMLC5k7J7WpTeVZGeQM3Akm2LnbcAS5QMkSyhSZWj0n3e kGRdmCBdRRCGQVsk2qeTahYZvtv1hJRe0mUbDEC/DWSDFLJudbSa5GAAQKBgQDxMFGpiQZ5IWrxh4Rh2CPJpZB1UfQjC5NI0kcKx8jXGp/GXoGfpcjmmF7yRg9ASeG1oOioZKdktSo7KRsSLKetG L3W pHkPaPMz/Vt788XuWAZKlBUJ0 02czeRjXDXY0B6Ik4uJ9okRuKO4EndWVhT/bSC2f6khergOe6HPqXwKBgQCwDbllyfGsRS0yn31KcNTjG2E9FMh9uhRP 0wcJR1dDqsXimQ1KE1cdWRsGWolA5oXaR8jNtSUc3NhuyYnjSoI/S2Z8VYnKdfDNb8x6pPV1RxCW5gRT2vrRxBnnN9bkI5ABhRKQslmAq I0mYAueUGU 6c7iwNIfC8U Kn 9gwAQKBgQCCQqloGdRAKXc7uQgbXAOADYYmhruHDeJe6wppXRswaXWvSi1RztThDZwB1yq3eu HC7976tipQFrtlrbDKxDoIm6DT8YJHta64l/wigujjFEA9dyfpO04GC7dkuKCiwey9AhzSYIvfirdIAfkwGWxGkUxphrWCk9Jq0vTUBICmwKBgB62djZssW11L/pZ2ninEGyCNUd7nbJZSPvfAhsS2nmGepCDwxGG82AC1r8I /xzEWmuHBF/mjw/m8xb4r8ZoFCrIk5tzLLOWOakNLOXkazHHcPxyKiUa2ZDIniA5HJL2JUQum9uEUZrh4Xd9o9/3pVpBQJ5hlPQLPgdxje59rABAoGAbXZS1iNDOJCw FG2YsCxUWl0fqwW41ldT6qplckCZ/oZuMNXdxCI3ZNhS1juo0A9dL4tu1wJqHdenlYlmxcVpXfz65n2mdXIULnW4eQts5qKyxY/DImJbrxi0qrRGLd0C/GMxpChjoWLrT4in87JI9PiuJT8OpNq/kgs/z3V4VQ=
Restored public key equals original: true
Restored private key equals original: true

以RSA算法为例,它的密钥有256/512/1024/2048/4096等不同的长度。长度越长,密码强度越大,当然计算速度也越慢

如果修改待加密的byte[]数据的大小,可以发现,使用512bit的RSA加密时,明文长度不能超过53字节,使用1024bit的RSA加密时,明文长度不能超过117字节,这也是为什么使用RSA的时候,总是配合AES一起使用,即用AES加密任意长度的明文,用RSA加密AES口令。

RSA算法的加密和解密操作中,密钥长度会影响可以处理的数据大小。一般来说,RSA加密操作能处理的最大数据块大小取决于密钥长度和填充模式。

在常见的填充模式下(如PKCS#1 v1.5或OAEP填充),RSA加密时需要对明文进行填充以满足算法要求的固定长度。这意味着,即使密钥长度较长,但实际能够加密的明文长度仍然受到限制

对于RSA加密,加密前需要对数据进行填充,以保证数据块的大小不超过密钥长度。填充的过程会增加一定的开销。一般来说,填充过程会使得实际能够加密的明文长度比密钥长度小一些。

具体来说,对于一个RSA密钥,它能够加密的最大数据块大小等于密钥长度减去一些填充的开销。因此,较短的密钥长度会限制加密的数据块大小。

在实际应用中,RSA算法的密钥长度一般选择较大的值(如2048位或更高),以提高安全性。但是,由于RSA算法加密的性能相对较慢,特别是在处理较大数据块时,因此通常不适合直接用于加密大量数据。相反,RSA常常与对称加密算法(如AES)结合使用,以提高性能和安全性。

因此,通常的做法是,使用RSA加密对称密钥(如AES密钥),然后使用对称密钥加密要传输的数据。这样既能保证安全性,又能提高加密和解密的性能。

小结

除了无法防止中间人攻击外,非对称加密算法还有一些其他缺点:

  1. 性能低下: 非对称加密算法的计算复杂度比对称加密算法高很多,特别是在处理大量数据时,性能会受到明显影响。因此,非对称加密算法通常用于密钥交换和数字签名等场景,而不适合直接加密大量数据。
  2. 密钥长度限制: 非对称加密算法的密钥长度会直接影响其安全性,通常需要选择较长的密钥长度以确保安全性。然而,较长的密钥长度会导致加密和解密的速度变慢,从而增加了计算的开销。
  3. 密钥管理复杂: 非对称加密算法需要管理公钥和私钥,密钥的生成、存储、分发和更新都需要一定的机制和流程来保证安全性。特别是在大规模系统中,密钥管理可能会变得非常复杂和困难。
  4. 安全性依赖于实现和使用: 非对称加密算法的安全性取决于其算法的设计和实现,以及密钥的生成和使用方式。如果实现存在漏洞或者密钥管理不当,可能会导致加密系统的安全性受到威胁。

综上所述,虽然非对称加密算法在密钥交换和数字签名等方面具有重要的作用,但其性能和安全性方面存在一些局限性,因此在实际应用中需要根据具体情况综合考虑,选择合适的加密方案。

0 人点赞