• RSA算法简介及JAVA与Python加解密


    1.RSA

    RSA加密算法,它是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)共同提出的一种加密算法,RSA就是他们三人姓氏开头字母拼在一起组成的。
    RSA算法是一种非对称加密算法,这一算法主要依靠分解大素数的复杂性来实现其安全性,由于大素数之积难被分解,因此该密码就难被破解。

    1.1 RSA 原理

    RSA基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。

    1.2 加密与解密

    非对称加密加密,最大的特点就是加密与解密使用的不是同一个秘钥。一般来说,秘钥在分发过程中,会有丢失的风险,这也就意味着别人可以获取秘钥,密文,因而可以解密得到明文。而非对称加密,公钥对所有人公开,使用公钥对明文加密得到密文。要想对密文进行解密,必须要使用私钥进行解密。私钥不需要发送给别人,只有解密者拥有。这样即使别人获取了密文,也没有私钥可以解密。降低了数据泄露的风险。

    1.3 签名与验签

    公钥私钥还有另外一种用途:签名。即用秘钥拥有者用私钥对数据进行签名,然后把数据发送出去,这时候所有人都可以获取到数据,但是数据是否被篡改了呢?拥有公钥的用户可以对私钥签名后的数据进行验证,这一步称为验签。如果签名验证通过,即可以认为数据是完整的。

    1.4 公钥长度

    我们经常会听到RSA-512, RSA-1024,这里的512,1024是什么呢?这里的1024指的是公钥的比特长度,即RSA-1024所生成的公钥长度为1024bit,即1024/8=128bytes。

    秘钥的长度越长,安全性越好,加密与解密的所需要的时间也就越长。密钥长度增加一倍,公钥操作所需时间增加约 4 倍,私钥操作所需时间增加约 8 倍,公私钥生成时间约增长 16 倍。

    RSA 算法密钥长度的选择是安全性和程序性能平衡的结果。不过也不要选择特别短的,首先JAVA要求不得低于512。其次,根据相关显示,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还未被破解,至少目前尚未有人公开宣布。(ps:一些非官方报道称,1024位的已经被解密)

    1.5 加密明文长度

    RSA算法一次能加密的明文长度与公钥长度相同,如RSA-1024算法一次可实际加密的最大明文长度为1024bits,生成的密文长度与公钥长度相同。但是如果明文长度小于1024bits怎么办?那就进行数据补齐,也就是padding。用padding,那么padding就会占据一定的位数,根据不同的padding标准,所占的位数也不同。常见的padding标准有NoPPadding、PKCS1Padding等。NoPPadding即意味着不占据长度,PKCS1Padding占据11个字节。一般情况下,我们会使用PKCS1Padding,这样可以让同样的明文使用相同的秘钥加密,也可以得到不同的密文。
    由于PKCS1Padding占据11个字节,那么RSA-1024算法一次能加密的明文的实际长度就是1024/8-11=117字节。这也就是我们为什么会说RSA-1024一次只能加密117个字节的内容,超过117个字节的内容需要多次加密。然后将加密内容连接起来。
    这样做得话解密怎么做呢?解密也要将拼接的字符串再拆开。由于一次生成的密文长度与公钥长度相同,因此可以轻而易举的将数据分段解密。

    2. JAVA应用

    由于为了方便,将密钥,密文等都用base64编码成字符串,这样方便copy等。

    import javax.crypto.Cipher;
    import javax.crypto.NoSuchPaddingException;
    import java.io.ByteArrayOutputStream;
    import java.io.UnsupportedEncodingException;
    import java.nio.charset.StandardCharsets;
    import java.security.InvalidKeyException;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.NoSuchAlgorithmException;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.RSAPublicKey;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.KeySpec;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    public class RSAUtil extends AbstractCrypto {
        private static final String RSA_ALGORITHM = "RSA";
        private static final int RSA_2048 = 2048;
    
        public void generateSecret() throws Exception {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            keyPairGenerator.initialize(RSA_2048);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            PublicKey publicKey = keyPair.getPublic();
            PrivateKey privateKey = keyPair.getPrivate();
    
            String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
            System.out.println("public key string: " + publicKeyString);
    
            String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
            System.out.println("private key String: " + privateKeyString);
        }
    
        public String encrypt(String plaintext, String key) throws Exception {
            return publicKeyEncrypted(plaintext, key);
        }
    
        public String decrypt(String ciphertext, String key) throws Exception {
            return privateDecrypt(ciphertext, key);
        }
    
        public String publicKeyEncrypted(String plaintext, String publicKeyString) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
            RSAPublicKey publicKey = getPublicKey(publicKeyString);
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.getEncoder().encodeToString(rsaCodec(cipher, plaintext.getBytes(StandardCharsets.UTF_8), publicKey.getModulus().bitLength() / 8 - 11));
        }
    
        private RSAPublicKey getPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            KeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString));
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        }
    
        private static RSAPrivateKey getPrivateKey(String privateKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            KeySpec pkcS8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString));
            return (RSAPrivateKey) keyFactory.generatePrivate(pkcS8EncodedKeySpec);
        }
    
        public static String privateDecrypt(String ciphertext, String privateKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException {
            RSAPrivateKey privateKey = getPrivateKey(privateKeyString);
    
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(
                    rsaCodec(cipher, Base64.getDecoder().decode(ciphertext), privateKey.getModulus().bitLength() / 8), StandardCharsets.UTF_8
            );
        }
    
        private static byte[] rsaCodec(Cipher cipher, byte[] data, int maxBlock) {
            int offset = 0;
            byte[] buffer;
            int i = 0;
    
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                while (data.length > offset) {
                    if (data.length - offset > maxBlock) {
                        buffer = cipher.doFinal(data, offset, maxBlock);
                    } else {
                        buffer = cipher.doFinal(data, offset, data.length - offset);
                    }
                    byteArrayOutputStream.write(buffer, 0, buffer.length);
                    i++;
                    offset = i * maxBlock;
                }
                return byteArrayOutputStream.toByteArray();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    3. Python应用

    Python应用安装了第三方的依赖:

    pip3 install pycryptodome
    
    • 1

    Python3代码如下

    import Crypto.Util.number
    from Crypto import Random
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
    from Crypto.Cipher import AES
    import base64
    import os
    import sys
    
    encoding_utf8 = 'utf-8'
    PRIVATE_KEY_BEGIN = '-----BEGIN PRIVATE KEY-----'
    PRIVATE_KEY_END = '-----END PRIVATE KEY-----'
    PUBLIC_KEY_BEGIN = '-----BEGIN PUBLIC KEY-----'
    PUBLIC_KEY_END = '-----END PUBLIC KEY-----'
    
    
    def rsa_create_key(bits):
        random_generator = Random.new().read
        rsa = RSA.generate(bits, random_generator)
    
        pkcs8_private_key = rsa.exportKey(format='PEM', passphrase=None, pkcs=8, protection=None)
        private_key_with_title_and_bottom = pkcs8_private_key.decode("utf-8")
        private_key_string = private_key_with_title_and_bottom.removeprefix(PRIVATE_KEY_BEGIN) \
            .removesuffix(PRIVATE_KEY_END).replace('\n', "")
    
        public_pem = rsa.publickey().exportKey()
        public_key_with_begin_and_end = public_pem.decode(encoding_utf8)
        public_key_string = public_key_with_begin_and_end.removeprefix(PUBLIC_KEY_BEGIN) \
            .removesuffix(PUBLIC_KEY_END).replace("\n", "")
    
        with open("private_key_string.pem", 'wb') as f:
            f.write(private_key_string.encode(encoding_utf8))
    
        return public_key_string, private_key_string
    
    
    def rsa_public_key_encrypt(plaintext, public_key_string):
        rsa_key = RSA.importKey(base64.b64decode(public_key_string))
        cipher = Cipher_pkcs1_v1_5.new(rsa_key)
    
        max_block = int(Crypto.Util.number.size(rsa_key.n) / 8 - 11)
        length = len(plaintext)
        offset = 0
        res = []
        plaintext_bytes = plaintext.encode(encoding_utf8)
        while length - offset > 0:
            if length - offset > max_block:
                res.append(cipher.encrypt(plaintext_bytes[offset:offset + max_block]))
            else:
                res.append(cipher.encrypt(plaintext_bytes[offset:]))
            offset += max_block
    
        cipher_text = base64.b64encode(b''.join(res))
        return cipher_text.decode(encoding_utf8)
    
    def rsa_private_key_decrypt(cipher_text):
        with open("private_key_string.pem", 'rb') as f:
            private_key_string = f.read()
        private_key = RSA.importKey(base64.b64decode(private_key_string))
        cipher = Cipher_pkcs1_v1_5.new(private_key)
    
        block_size = int(Crypto.Util.number.size(private_key.n) / 8)
        cipher_text_bytes = base64.b64decode(cipher_text)
        length = len(cipher_text_bytes)
        offset = 0
        res = []
    
        while length - offset > 0:
            if length - offset > block_size:
                res.append(cipher.decrypt(cipher_text_bytes[offset: offset + block_size], "ERROR"))
            else:
                res.append(cipher.decrypt(cipher_text_bytes[offset:], "ERROR"))
            offset += block_size
    
        plaintext = b''.join(res)
        return plaintext.decode(encoding_utf8)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    4.总结

    上述JAVA与Python代码,可以实现JAVA生成公钥密钥,JAVA用公钥解密,Python用私钥解密,反之亦可。
    Python要注意设置私钥格式pkcs8,默认是pkcs1。为了和JAVA互用,这里设置格式为pkcs8。
    另外,很多例子都是生成pem文件,这里我只是把文件里内容读写出来。

  • 相关阅读:
    《006.Springboot+vue之旅游信息推荐系统》【有文档】
    SwiftUI ☞ @State 相关问题
    Unity 切换场景后场景变暗
    Docker 镜像管理
    如果再来一次,你还会选择互联网么?
    docker安装seata(指定配置文件、数据库、容器数据卷等)
    反序列化漏洞详解
    hutool工具包快速入门
    【JAVA】-- 简易超市管理系统窗口(五)(实现思路+每步代码)
    武器检测YOLOV8NANO
  • 原文地址:https://blog.csdn.net/Apple_wolf/article/details/127912972