API接口加密—非对称加密解密之分段加解密

引言:


对称加密:双方使用的同一个密钥,既可以加密又可以解密,这种加密方法称为对称加密,也称为单密钥加密。
优点:速度快,对称性加密通常在消息发送方需要加密大量数据时使用,算法公开、计算量小、加密速度快、加密效率高。

缺点:在数据传送前,发送方和接收方必须商定好秘钥,然后 使双方都能保存好秘钥。其次如果一方的秘钥被泄露,那么加密信息也就不安全了。
另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘 钥
,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。

在对称加密算法中常用的算法有:DES、AES等。

AES:密钥的长度可以为128、192和256位,也就是16个字节、24个字节和32个字节

DES:密钥的长度64位,8个字节。
非对称加密:一对密钥由公钥和私钥组成(可以使用很多对密钥)。私钥解密公钥加密数据,公钥解密私钥加密数据(私钥公钥可以互相加密解密)。

私钥只能由一方保管,不能外泄。公钥可以交给任何请求方。

在非对称加密算法中常用的算法有: 

RSA、Elgamal、背包算法、Rabin、Diffie-Hellman、ECC(椭圆曲线加密算法)。
使用最广泛的是RSA算法,Elgamal是另一种常用的非对称加密算法。

缺点:速度较慢

优点:安全

较为常用的用法:公钥加密私钥加密;私钥加签公钥解签;

下面主要介绍非对称加密中的RSA分段加密,分段解密;
为什么会用到分段加密呢?因为客户端传输密文中的明文超过127个字符在使用 RSA加密解密内容时会出现这样的异常 : Data must not be longer than 117 bytes。 
解决办法是:分段加密和分段解密。

下面是例子 :具体用法请看main方法;(项目中已实践)


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

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 *
 * @author xxj
 */
public class RsaHelper {


    public static int enSegmentSize = 117;

    public static int deSegmentSize = 128;
    /**
     * 生成公钥、私钥对(keysize=1024)
     * @return
     */
    public RsaHelper.KeyPairInfo getKeyPair() {
        return getKeyPair(1024);
    }
    /**
     * 生成公钥、私钥对
     * @param keySize
     * @return
     */
    public RsaHelper.KeyPairInfo getKeyPair(int keySize) {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥对生成器,密钥大小一般要大于1024位,
            keyPairGen.initialize(keySize);
            // 生成一个密钥对,保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            // 得到私钥
            RSAPrivateKey oraprivateKey = (RSAPrivateKey) keyPair.getPrivate();
            // 得到公钥
            RSAPublicKey orapublicKey = (RSAPublicKey) keyPair.getPublic();

            RsaHelper.KeyPairInfo pairInfo = new RsaHelper.KeyPairInfo(keySize);
            //公钥
            byte[] publicKeybyte = orapublicKey.getEncoded();
            String publicKeyString = Base64.encodeBase64String(publicKeybyte);
            pairInfo.setPublicKey(publicKeyString);
            //私钥
            byte[] privateKeybyte = oraprivateKey.getEncoded();
            String privateKeyString = Base64.encodeBase64String(privateKeybyte);
            pairInfo.setPrivateKey(privateKeyString);

            return pairInfo;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 获取公钥对象
     * @param publicKeyBase64
     * @return
     */
    public static PublicKey getPublicKey(String publicKeyBase64)
            throws InvalidKeySpecException,NoSuchAlgorithmException {

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicpkcs8KeySpec =
                new X509EncodedKeySpec(Base64.decodeBase64(publicKeyBase64));
        PublicKey publicKey = keyFactory.generatePublic(publicpkcs8KeySpec);
        return publicKey;
    }
    /**
     * 获取私钥对象
     * @param privateKeyBase64
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(String privateKeyBase64)
            throws NoSuchAlgorithmException, InvalidKeySpecException{
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec privatekcs8KeySpec =
                new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyBase64));
        PrivateKey privateKey = keyFactory.generatePrivate(privatekcs8KeySpec);
        return privateKey;
    }
    /**
     * 使用共钥加密
     * @param content 待加密内容
     * @param publicKeyBase64  公钥 base64 编码
     * @return 经过 base64 编码后的字符串
     */
    public String encipher(String content,String publicKeyBase64){
        return encipher(content,publicKeyBase64,-1);
    }
    /**
     * 使用共钥加密(分段加密)
     * @param content 待加密内容
     * @param publicKeyBase64  公钥 base64 编码
     * @param segmentSize分段大小,一般小于 keySize/8(段小于等于0时,将不使用分段加密)
     * @return 经过 base64 编码后的字符串
     */
    public static String encipher(String content,String publicKeyBase64,int segmentSize){
        try {
            PublicKey publicKey = getPublicKey(publicKeyBase64);
            return encipher(content,publicKey,segmentSize);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 分段加密
     * @param ciphertext 密文
     * @param key 加密秘钥
     * @param segmentSize 分段大小,<=0 不分段
     * @return
     */
    public static String encipher(String ciphertext,java.security.Key key,int segmentSize){
        try {
            // 用公钥加密
            byte[] srcBytes = ciphertext.getBytes();

            // Cipher负责完成加密或解密工作,基于RSA
            Cipher cipher = Cipher.getInstance("RSA");
            // 根据公钥,对Cipher对象进行初始化
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] resultBytes = null;

            if(segmentSize>0)
                resultBytes = cipherDoFinal(cipher,srcBytes,segmentSize); //分段加密
            else
                resultBytes = cipher.doFinal(srcBytes);

            String base64Str =  Base64.encodeBase64String(resultBytes);
            return base64Str;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 分段大小
     * @param cipher
     * @param srcBytes
     * @param segmentSize
     * @return
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    private static byte[] cipherDoFinal(Cipher cipher,byte[] srcBytes,int segmentSize)
            throws IllegalBlockSizeException, BadPaddingException, IOException {
        if(segmentSize<=0)
            throw new RuntimeException("分段大小必须大于0");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int inputLen = srcBytes.length;
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > segmentSize) {
                cache = cipher.doFinal(srcBytes, offSet, segmentSize);
            } else {
                cache = cipher.doFinal(srcBytes, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * segmentSize;
        }
        byte[] data = out.toByteArray();
        out.close();
        return data;
    }
    /**
     * 使用私钥解密
     * @param contentBase64 待加密内容,base64 编码
     * @param privateKeyBase64  私钥 base64 编码
     * @segmentSize 分段大小
     * @return
     */
    public String decipher(String contentBase64,String privateKeyBase64){
        return decipher(contentBase64, privateKeyBase64,-1);
    }
    /**
     * 使用私钥解密(分段解密)
     * @param contentBase64 待加密内容,base64 编码
     * @param privateKeyBase64  私钥 base64 编码
     * @segmentSize 分段大小
     * @return
     */
    public static String decipher(String contentBase64, String privateKeyBase64, int segmentSize){
        try {
            PrivateKey privateKey = getPrivateKey(privateKeyBase64);
            return decipher(contentBase64, privateKey,segmentSize);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 分段解密
     * @param contentBase64 密文
     * @param key 解密秘钥
     * @param segmentSize 分段大小(小于等于0不分段)
     * @return
     */
    public static String decipher(String contentBase64,java.security.Key key,int segmentSize){
        try {
            // 用私钥解密
            byte[] srcBytes = Base64.decodeBase64(contentBase64);
            // Cipher负责完成加密或解密工作,基于RSA
//            Cipher deCipher = Cipher.getInstance("RSA/ECB/NoPadding");
            Cipher deCipher = Cipher.getInstance("RSA");

            // 根据公钥,对Cipher对象进行初始化
            deCipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decBytes = null;//deCipher.doFinal(srcBytes);
            if(segmentSize>0)
                decBytes = cipherDoFinal(deCipher,srcBytes,segmentSize); //分段加密
            else
                decBytes = deCipher.doFinal(srcBytes);

            String decrytStr=new String(decBytes);
            return decrytStr;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 秘钥对
     *
     */
    public class KeyPairInfo{
        public KeyPairInfo(int keySize){
            setKeySize(keySize);
        }
        public KeyPairInfo(String publicKey,String privateKey){
            setPrivateKey(privateKey);
            setPublicKey(publicKey);
        }
        String privateKey;
        String publicKey;
        int keySize=0;
        public String getPrivateKey() {
            return privateKey;
        }
        public void setPrivateKey(String privateKey) {
            this.privateKey = privateKey;
        }
        public String getPublicKey() {
            return publicKey;
        }
        public void setPublicKey(String publicKey) {
            this.publicKey = publicKey;
        }
        public int getKeySize() {
            return keySize;
        }
        public void setKeySize(int keySize) {
            this.keySize = keySize;
        }
    }

    public static void main(String[] args) {

        String privatekey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALxtvRWQzjxDKeearhwCTDfBoCx9S7O1deaGO3oajxM7MfoYSt3Y0Uwk4//Pnrvf5Ewie2XcmgIubvAOAbctw9wL7gDF0fb7pSI8kfvv/6gBEt6Kl0ZVkUhMftzSqo7urkxzGfz7HXiV6IMJaU60WM7uB7nEmrbMG8+DA5AikH3bAgMBAAECgYATwsp2rYYDaePGJ0GlLFSD8Gl04PsEcL5Zm7A3IPvqP2YBXXTUiT2B58iFdmemOle96EvapeT835PA1yc057bHebwYtIvrGlXj8Km+EIVwl4IZnq6npJKsw7x+KXvm7qw2kfZGdmNW7DqYWvrBZzIqk+/s1Vcx7S03k71S4FpdeQJBAO5fcEGMNmXDFlPb/jVu1fgXGL116C3oe2jX5eRttvXaEFKsy9a2eOquU6uUp4WHoLXJFjL+dzpY53VBCdQ/sWUCQQDKXNK337VAvHQuOds6YRpCZJTd2BcnPOH0s6tucK0dxdkxC2b6GM4laoKXtWz3of8sMfI5mBqbN3/Wc5MrEh4/AkEAnTVIPZKRVa/CC/BsySSd7Q6efGVEiPJoYY7xySMqpR354eygSqzxNiVefSx/ByuZrApAn8T/MPXtQIFyUA7pmQJAfz2OYX+QgHv+sXI/np2f3U9RK4zMQifODAdNK8w9jhLxVh1NSsR+Gpi2NJ4nPi3mPOvK2twBHsfu4fEaAGMMEQJBAKkk1Nnq447twtrtCCGxy4ShJuposQQxXjFqdyx2mMGnRVWqL1t5dHZTuJno6rhs70A3Ehk1Vj9FM5ykz58rr0s=";
        String  publickey= "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8bb0VkM48Qynnmq4cAkw3waAsfUuztXXmhjt6Go8TOzH6GErd2NFMJOP/z5673+RMIntl3JoCLm7wDgG3LcPcC+4AxdH2+6UiPJH77/+oARLeipdGVZFITH7c0qqO7q5Mcxn8+x14leiDCWlOtFjO7ge5xJq2zBvPgwOQIpB92wIDAQAB";
        String str = "非对称加密:一对密钥由公钥和私钥组成(可以使用很多对密钥)。私钥解密公钥加密数据,公钥解密私钥加密数据(私钥公钥可以互相加密解密)。私钥只能由一方保管,不能外泄。公钥可以交给任何请求方。在非对称加密算法中常用的算法有: RSA、Elgamal、背包算法、Rabin、Diffie-Hellman、ECC(椭圆曲线加密算法)。使用最广泛的是RSA算法,Elgamal是另一种常用的非对称加密算法。";

        String entrystr = encipher(str, publickey, enSegmentSize);
        System.out.println("entrystr:" + entrystr);


        System.out.println("decipher:" + decipher(entrystr, privatekey, deSegmentSize));





    }

}

main方法打印结果

最后

当出现前端加解密没问题,后台加解密没问题,但是前端不能完全解密后台的密文,前端给的密文后台解不出的问题。
原因:前端和后台生产秘药对采用的补位方式不同,所以出现加解密失败。


解决方案:
使用模和指数生成RSA公钥 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA/None/NoPadding】
前端修改代码:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

请多多指教,有问题请给我留言。

发布了48 篇原创文章 · 获赞 7 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/maguoliang110/article/details/84342405