美文网首页技术干货程序员
RSA公钥私钥的简单实现

RSA公钥私钥的简单实现

作者: cen_s | 来源:发表于2017-04-21 23:18 被阅读0次

    RSA算法是种能同时用于加密和数字签名的算法,也是被研究得最广泛的公钥算法。而公钥私钥的加密解密也会使一些小伙伴疑惑,这里稍微简单说一下,再简单地实现一下。

    首先,公钥加密私钥可以解密。其次,私钥加密公钥可以解密。再深一步来说,公钥负责加密,私钥负责解密。私钥负责签名,公钥负责验证。公钥就是给大家用的,私钥就是给自己用的,必须小心保存。举个栗子,A想向B发送一条加密的信息,使用B的公钥加密,这样只有拥有对应私钥的B才能解密,确保了信息没有被别人看到,其次A可以用自己的私钥加密一条信息发送给B,B用A的公钥验证,因为只有A拥有私钥所以能证明此信息是A所发的。(如果A是在借钱B经过公钥验证后就知道是本人而不是骗子了)

    RSAUtils

    首先定义一些全局用的常量变量。

    public static final int ENCRYPT_MODE = Cipher.ENCRYPT_MODE;
    
    public static final int DECRYPT_MODE = Cipher.DECRYPT_MODE;
        
    public static final Charset UTF8 = Charset.forName("UTF-8");
        
    private static final String ALGORITHM = "RSA";
        
    private Cipher cipher;
    

    其次是调用公钥私钥的方法。

        private static Key getEncKey(String encKey){
            PublicKey publicKey = null;
            try {
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(encKey.getBytes(UTF8)));
                KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
                publicKey = keyFactory.generatePublic(keySpec);
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return publicKey;
        }
        
        private static Key getDecKey(String decKey){
            PrivateKey privateKey = null;
            try {
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(decKey.getBytes(UTF8)));
                KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
                privateKey = keyFactory.generatePrivate(keySpec);
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return privateKey;
        }
    

    定义一下RSAUtils的init方法

        public boolean init(int mode, String key){
            //参数校验code
            try {
                cipher = Cipher.getInstance(ALGORITHM);
            } catch (NoSuchAlgorithmException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (NoSuchPaddingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            Key secKey = null;
            //靠mode判断是加密还是解密
            if (mode == ENCRYPT_MODE) {
                secKey = getEncKey(key);
            }
            else if (mode == DECRYPT_MODE) {
                secKey = getDecKey(key);
            }
            if (secKey == null) {
                return false;
            }
            try {
                cipher.init(mode, secKey);
            } catch (InvalidKeyException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return false;
            }
            return true;
        }
    

    加解密的方法主体

        public String encrypt(String text){
            byte[] data = null;
            try {
                data = cipher.doFinal(text.getBytes(UTF8));
            } catch (IllegalBlockSizeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BadPaddingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new String(Base64.getEncoder().encode(data), UTF8);
        }
        
        public String decrypt(String text){
            byte[] data = null;
            try {
                data = cipher.doFinal(Base64.getDecoder().decode(text.getBytes(UTF8)));
            } catch (IllegalBlockSizeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BadPaddingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new String(data, UTF8);
        }
    

    RSATest

    测试代码

            String password = "HelloWorld!";
            RSAUtils rsaUtils = new RSAUtils();
            String enckey = pubkey;//公钥
            rsaUtils.init(Cipher.ENCRYPT_MODE, enckey);
            //得到密文
            String cipherText = rsaUtils.encrypt(password);
            System.out.println("cipherText: " + cipherText);
            String deckey = prikey;//私钥
            rsaUtils.init(Cipher.DECRYPT_MODE, deckey);
            //得到明文
            String plaintext = rsaUtils.decrypt(cipherText);
            System.out.println("plaintext: " + plaintext);
    

    结果

    cipherText: JF3tcjS2P552QJQQgu7mTTBS4NsW7pCZdbi3bKaDonyEqBXuRiM6Ctm4kSzw9fkb1dYciyOcS0I+KbS9sopGNTGjr9ExwB+/0OvNL9HPWlfG35NdQPK+0Vm4YD1oBrRap9EWGYyR6QC2vV4wesYOkXNn8gjosIew/4gSoMhL3E4=
    plaintext: HelloWorld!
    

    关于Base64
    现在在Java 8中java.util包下面实现了Base64可以直接拿来用,本文用的也是java.util.Base64。Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。举个栗子,11111111, 11111111, 11111111 转换后 00111111, 00111111, 00111111, 00111111 (二进制)三个字节的是原文,四个字节的是转换后的Base64编码,其前两位均为0。再匹配Base64编码表得到对应的编码。

    索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符
    ----|------|----
    0 | A | 17 | R | 34 | i | 51 | z
    1 | B | 18 | S | 35 | j | 52 | 0
    2 | C | 19 | T | 36 | k | 53 | 1
    3 | D | 20 | U | 37 | l | 54 | 2
    4 | E | 21 | V | 38 | m | 55 | 3
    5 | F | 22 | W | 39 | n | 56 | 4
    6 | G | 23 | X | 40 | o | 57 | 5
    7 | H | 24 | Y | 41 | p | 58 | 6
    8 | I | 25 | Z | 42 | q | 59 | 7
    9 | J | 26 | a | 43 | r | 60 | 8
    10 | K | 27 | b | 44 | s | 61 | 9
    11 | L | 28 | c | 45 | t | 62 | +
    12 | M | 29 | d | 46 | u | 63 | /
    13 | N | 30 | e | 47 | v
    14 | O | 31 | f | 48 | w
    15 | P | 32 | g | 49 | x
    16 | Q | 33 | h | 50 | y

    二进制数据不可能每次都刚好平均地分为6位,这时候,就在数据末尾填充0,使二进制数据的长度成为24的倍数(6和8的最小公倍数)。末尾全部填充的就由"="表示。由于标准的Base64编码后可能出现字符+和/,这两个在URL中不能直接作为参数,这时候可以用UrlEncoder,可以把字符+和/分别变成-和_。

    测试代码

            String encoded = null;
            try {
                encoded = Base64.getEncoder().encodeToString("Hello World!!!".getBytes("UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(encoded);
            byte[] decoded = Base64.getDecoder().decode(encoded);
            try {
                System.out.println(new String(decoded, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                encoded = Base64.getEncoder().encodeToString("He>lo?World!!!".getBytes("UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(encoded);
            String urlEncoded = null;
            try {
                urlEncoded = Base64.getUrlEncoder().encodeToString("He>lo?World!!!".getBytes("UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(urlEncoded);
    

    结果

    SGVsbG8gV29ybGQhISE=
    Hello World!!!
    SGU+bG8/V29ybGQhISE=
    SGU-bG8_V29ybGQhISE=
    

    相关文章

      网友评论

        本文标题:RSA公钥私钥的简单实现

        本文链接:https://www.haomeiwen.com/subject/ecujzttx.html