Java 中常用的 非对称加密算法 的使用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/bestcxx/article/details/99933509

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好。

非对称加密

非对称加密一般需要算法支持一对公私钥的相互加解密,即公钥加密私钥可以机密,私钥加密,公钥可以解密。
一般私钥不会对外公开,公钥会对外公开。
由于非对称加密的解密过程需要更多的时间,所以一般仅用于少量数据的加密——延伸来说,可以用于验签。

KeyPairGenerator

JDK 的 java.security.KeyPairGenerator 为我们提供了生成 公私钥的方法

JDK 支持的非对称加密算法和私钥长度

在 java.security.KeyPairGenerator 的注释中可以看到,JDK 规定,所有的JAVA 平台必须支持下面的算法和秘钥长度
秘钥长度只是一个目标长度,实际产生的秘钥长度一般是近似值——比如1024,则实际的可能是1023,1218
显然,秘钥长度越长,公私钥对长生的时间也越长,加解密也需更多时间

 * <li>{@code DiffieHellman} (1024)</li>
 * <li>{@code DSA} (1024)</li>
 * <li>{@code RSA} (1024, 2048)</li>

公钥和私钥的形式

公钥和私钥都被称为秘钥
公钥和私钥本质就是一个随机的字节数组,其展示形式一般会转化为 Base64 字符串,也是存储的形式。
由于在实际使用中我们并不是以字符串形式直接使用的,所有就涉及到存储形式和使用形式之间的转化。

公钥和私钥的类和相关方法

JDK 分别为 公钥和私钥提供了Java类
java.security.PublicKey 和 java.security.PrivateKey
二者都有三个属性 秘钥的加密方法名称 Algorithm、秘钥的字节数组Encoded、秘钥的规范标准 Format

  • getAlgorithm():返回该秘钥相关的算法的标准名称,比如RSA\DSA\DiffieHellman
  • getFormat():返回秘钥私有的编码字节码的名称,如果不支持编码,则返回null。
    该编码名称需要得到 ASN.1 的认证,比如对于公钥其编码名称叫做
    SubjectPublicKeyInfo,其定义位于 X.509 标准,公钥的该值就返回 X.509
    类似的,对于私钥,其 ASN.1 对应编码名称为 PrivateKeyInfo,对应的句法规范为
    PKCS #8 ,对于私钥,该属性返回 PKCS#8
  • getEncoded():返回满足 getFormat()编码标准的字节码数组,如果 getFormat()不允许编码,则这里返回null
    public String getAlgorithm();
    public String getFormat();
    public byte[] getEncoded();

公私钥的生成、保存和恢复

非对称加密算法需要生成公钥和私钥,生成时需要指定算法和期望的私钥长度。

生成公私钥对

上文已经说到,非对称加密的公私钥对的生成有几种 RSA、DSA、DiffieHellman,并且可以指定私钥的长度——尽管生成值是一个指定长度的近似值。
下面的例子提供了两种方法,第一种方法直接返回Base64字符串格式的 公私钥,第二种方法则返回公私钥的默认格式

/**
     * 生成 非对称加密算法 公私钥对的方法-返回默认秘钥格式
     * @param algorithm 加密算法
     * @param keySize   私钥长度-实际会生成一个近似值
     * <li>{@code DiffieHellman} (1024)</li>
     * <li>{@code DSA} (1024)</li>
     * <li>{@code RSA} (1024, 2048)</li>
     * @return
     * @throws NoSuchAlgorithmException
     */
    private static Map<Class,Object> initKeyPair(String algorithm,int keySize) throws NoSuchAlgorithmException {
        //指定算法 RSA、DiffieHellman、DSA
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(algorithm);
        //指定私钥的长度——实际生成的也是近似值
        keyPairGenerator.initialize(keySize);
        //生成公私钥对
        KeyPair kPair=keyPairGenerator.generateKeyPair();
        PrivateKey privateKey=kPair.getPrivate();
        PublicKey publicKey=kPair.getPublic();

        Map<Class,Object> keyPairMap=new HashMap<Class,Object>();
        keyPairMap.put(PrivateKey.class,privateKey);
        keyPairMap.put(PublicKey.class,publicKey);
        return keyPairMap;
    }

    /**
     * 生成 非对称加密算法 公私钥对的方法-返回字符Base64 字符串格式
     * @param algorithm 加密算法
     * @param keySize   私钥长度-实际会生成一个近似值
     * <li>{@code DiffieHellman} (1024)</li>
     * <li>{@code DSA} (1024)</li>
     * <li>{@code RSA} (1024, 2048)</li>
     * @return
     * @throws NoSuchAlgorithmException
     */
    private static Map<Class,Object> initKeyPairBase64Str(String algorithm,int keySize) throws NoSuchAlgorithmException {
        //指定算法 RSA、DiffieHellman、DSA
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(algorithm);
        //指定私钥的长度——实际生成的也是近似值
        keyPairGenerator.initialize(keySize);
        //生成公私钥对
        KeyPair kPair=keyPairGenerator.generateKeyPair();
        PrivateKey privateKey=kPair.getPrivate();
        PublicKey publicKey=kPair.getPublic();

        //以 Base64 格式输出 公私钥
        String privateKeyStr= Base64.encodeBase64String(privateKey.getEncoded());

        String publicKeyStr=Base64.encodeBase64String(publicKey.getEncoded());
        System.out.println(privateKey.getEncoded().length);
        System.out.println(publicKey.getEncoded().length);
        //System.out.println("私钥:"+"algorithm:"+privateKey.getAlgorithm()+";format:"+privateKey.getFormat()+";私钥base64:"+privateKeyStr);
        //System.out.println("公钥:"+"algorithm:"+publicKey.getAlgorithm()+";format:"+publicKey.getFormat()+";公钥base64:"+publicKeyStr);

        Map<Class,Object> keyPairMap=new HashMap<Class,Object>();
        keyPairMap.put(PrivateKey.class,privateKeyStr);
        keyPairMap.put(PublicKey.class,publicKeyStr);
        return keyPairMap;
    }

公钥的恢复

公钥一般遵循 .509 规范
下面给出公钥的恢复办法

 /**
     * 由Base64字符编码恢复 公钥
     * @param pubKeyBase64Str    公钥 Base64 编码 字符串
     * @param algorithm          算法名称,如 RSA\DSA\DiffieHellman
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey restorePublicKey(String pubKeyBase64Str,String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //将Base64 字符串转化为字节数组
        byte[] keyBytes=Base64.decodeBase64(pubKeyBase64Str);
        //
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        //指定算法 比如 RSA
        KeyFactory factory = KeyFactory.getInstance(algorithm);
        //
        PublicKey publicKey = factory.generatePublic(x509EncodedKeySpec);
        return publicKey;
    }

私钥的恢复

私钥句法规范为 PKCS#8,
下面给出公钥的恢复办法

/**
     * 由Base64字符编码恢复 私钥
     * @param privateBase64Str 私钥 Base64 编码字符串
     * @param algorithm        算法名称,如 RSA\DSA\DiffieHellman
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey restorePrivateKey(String privateBase64Str,String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //将Base64 字符串转化为字节数组
        byte[] keyBytes=Base64.decodeBase64(privateBase64Str);
        //私钥标准
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //指定算法,比如 RSA
        KeyFactory factory = KeyFactory.getInstance(algorithm);
        //获取私钥
        PrivateKey privateKey = factory.generatePrivate(pkcs8EncodedKeySpec);
        return privateKey;
    }

加解密过程

加密过程

加密一般使用公钥

 /**
     * 将 字符串 用指定加密算法 加密
     * @param publicKey        公钥
     * @param algorithm        算法,如 RSA
     * @param strBytes              待加密字节数组
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
    public static byte[] encrypt(PublicKey publicKey,String algorithm,byte[] strBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        //获取 cipher 对象,指定加密算法 比如 RSA
        Cipher cipher = Cipher.getInstance(algorithm);
        //初始化 cipher 对象,指定为加密模式,指定公钥
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(strBytes);
    }

解密过程

解密一般使用私钥

/**
     *  将 字符串 用指定加密算法 解密
     * @param privateKey     私钥
     * @param algorithm      算发,如 RSA
     * @param strBytes       待解密字节数组
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws UnsupportedEncodingException
     */
    public static byte[] decipher(PrivateKey privateKey,String algorithm, byte[] strBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException{
        Cipher cipher=Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE,privateKey);
        return cipher.doFinal(strBytes);
    }

具体算法

工具类方法

在涉及到非对称加密相关的内容时,这个工具类都将起到基础的作用。
非对称加密基础工具类

RSA

  • RSA密码体制是一种公钥密码体制,加密算法公开,以分配的密钥作为加密解密的关键。一般来说,在一对公私钥中,公钥和私钥都可以用来加密和解密,即公钥加密能且只能被对应的私钥进行解密,私钥加密能且只能被对应的公钥进行解密。但我们一般都用公钥加密,私钥解密,而且生成的私钥往往会比公钥蕴含了更多的信息量。(这里说的加密肯定是可逆的,不然直接销毁就可以了没必要再去加密,加密是为了保障数据的安全和验证身份。)
  • RSA 会限制加解密字符串长度,加密字符串长度小于等于117 ,解密长度小于等于128
    RFC2246:美国政府不允许包含 大于 512位key的 RSA 加密模块的软件的出口,但是并没有限制大于 512位key的RSA 算法用于数字签名。
  • 加解密过程使用字节数组,如果要用于打印,为了保证不是乱码,一般通过Base64作为过渡展示或保存加密结果的字节数组,即 原字符串-字节数组-加密-加密结果字节数组-加密结果Base64字符串-加密结果字节数组-解密-解密结果字节数组-原字符串
  • 非对称加密算法效率低,一般仅用于签名验证。-查看 加密验签
 public static void main(String [] args) throws NoSuchAlgorithmException {
        String algorithm="RSA";
        int keySize=1024;
        String str="123456789abc";
        String charset="utf8";

        Map<Class,Object> keyPairMap=initKeyPair(algorithm,keySize);
        PublicKey publicKey=(PublicKey)keyPairMap.get(PublicKey.class);
        PrivateKey privateKey=(PrivateKey) keyPairMap.get(PrivateKey.class);

        //加密过程
        try {
            System.out.println("待加密字符串:str="+str);
            //将加密结果转化为Base64编码
            String result= Base64.encodeBase64String(AsymmetricEncryptionUtil.encrypt(publicKey,algorithm,str.getBytes()));
            //加密后得Base64字符串
            System.out.println("加密后的字节数组转化为 Base64展示="+result);
            //将加密的Base64字符串解密-将解密字节数组转Base64字符串
            String ostr= new String(AsymmetricEncryptionUtil.decipher(privateKey,algorithm,Base64.decodeBase64(result)));
            System.out.println("原字符串="+ostr);
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }

    }
  • 结果
待加密字符串:str=123456789abc
加密后的字节数组转化为 Base64展示=BVSxdlkXI8xeqYBQylr6Uo1pinLpdSx6Ujj7Iou3ld6xj7uaq/6Z8hqD2jJaXKxNoLwN61df6vmC6aRGsJTTnnZ9W7zRiX/emuWzJw2XYXaEVq2jN9/HNWZFZH/6/1HHpET62Ngqb1bxLnBlF2EttT9GYDwaNmO9BqvFTS0Q0qM=
原字符串=123456789abc

猜你喜欢

转载自blog.csdn.net/bestcxx/article/details/99933509
今日推荐