加密解密(四)——非对称加密算法

一.什么是非对称加密算法

  非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
  相对于对称加密算法而言,非对称加密算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多。

二.常见的非对称加密算法

在非对称加密中使用的主要算法有:D-H、RSA、Elgamal、背包算法、Rabin、ECC(椭圆曲线加密算法)等。

X509EncodedKeySpec:此类表示公钥的ASN.1编码,根据ASN.1类型SubjectPublicKeyInfo进行编码。
PKCS8EncodedKeySpec:该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。

(1).DH算法

  传统上,双方之间的安全加密通信要求他们首先通过一些安全的物理通道(例如由信任的快递运送的纸钥匙列表)来交换密钥。Diffie-Hellman密钥交换方法允许没有彼此之前的知识的两方在不安全的信道上共同建立共享密钥。该密钥然后可以用于使用对称密钥密码来加密后续通信。

1.甲方构建密钥对儿,将公钥公布给乙方,将私钥保留;双方约定数据加密算法;乙方通过甲方公钥构建密钥对儿,将公钥公布给甲方,将私钥保留。
2.甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给乙方加密后的数据;乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。
3.乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给甲方加密后的数据;甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。

package demo.dh;

import java.security.Key;
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.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;

import org.junit.Test;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Demo01_DH {

    private String messageToB = "This is A TO B";
    private String messageToA = "This is B TO A";

    private final String ALGORITHM = "DH";

    public static final String SECRET_ALGORITHM = "DES";

    private final int KEY_SIZE = 512;

    private final String PUBLIC_KEY = "DHPublicKey";
    private final String PRIVATE_KEY = "DHPrivateKey";
    private BASE64Encoder base64Encoder = new BASE64Encoder();
    private BASE64Decoder base64Decoder = new BASE64Decoder();

    @Test
    public void testDH() throws Exception {
        Map<String, Object> aKeyMap = initKey();
        String aPublicKey = getPublicKey(aKeyMap);
        String aPrivateKey = getPrivateKey(aKeyMap);
        System.err.println("甲方公钥:\r" + aPublicKey);
        System.err.println("甲方私钥:\r" + aPrivateKey);
        Map<String, Object> bKeyMap = initKey(aPublicKey);
        String bPublicKey = getPublicKey(bKeyMap);
        String bPrivateKey = getPrivateKey(bKeyMap);
        System.err.println("乙方公钥:\r" + bPublicKey);
        System.err.println("乙方私钥:\r" + bPrivateKey);
        System.out.println("原文:" + messageToB);
        byte[] aCode = encrypt(messageToB.getBytes(), aPublicKey, bPrivateKey);
        System.out.println("密文:"+base64Encoder.encode(aCode));
        byte[] aDecode = decrypt(aCode, bPublicKey, aPrivateKey);
        String aOutput = (new String(aDecode));
        System.out.println("解密:" + aOutput);

        System.out.println(" ===============反过来加密解密================== ");  
        System.out.println("原文: " + messageToA);  
        // 由乙方公钥,甲方私钥构建密文  
        byte[] bCode = encrypt(messageToA.getBytes(), bPublicKey,aPrivateKey);  
        System.out.println("密文:"+base64Encoder.encode(bCode));
        // 由甲方公钥,乙方私钥解密  
        byte[] bDecode = decrypt(bCode, aPublicKey, bPrivateKey);  
        String bOutput = (new String(bDecode));  
        System.out.println("解密: " + bOutput);  
    }

    // 初始化密钥
    private Map<String, Object> initKey() throws NoSuchAlgorithmException {
        // 密钥对的生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        // 初始化密钥对生成器
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 甲方公钥
        PublicKey publicKey = keyPair.getPublic();
        // 甲方私钥
        PrivateKey privateKey = keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    public Map<String, Object> initKey(String key) throws Exception {
        // 解析甲方公钥
        byte[] keyBytes = base64Decoder.decodeBuffer(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

        // 由甲方公钥构建乙方密钥
        DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
        keyPairGenerator.initialize(dhParamSpec);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 乙方公钥
        PublicKey publicKey = keyPair.getPublic();
        // 乙方私钥
        PrivateKey privateKey = keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 加密
     * 
     * @param data
     *            待加密数据
     * @param publicKey
     *            甲方公钥
     * @param privateKey
     *            乙方私钥
     * @return
     * @throws Exception
     */
    private byte[] encrypt(byte[] data, String publicKey, String privateKey) throws Exception {
        // 生成本地密钥
        SecretKey secretKey = getSecretKey(publicKey, privateKey);
        // 数据加密
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

      /** 
     * 解密
     * 
     * @param data 
     *            待解密数据 
     * @param publicKey 
     *            乙方公钥 
     * @param privateKey 
     *            乙方私钥 
     * @return 
     * @throws Exception 
     */  
    public byte[] decrypt(byte[] data, String publicKey,  
            String privateKey) throws Exception {  

        // 生成本地密钥  
        SecretKey secretKey = getSecretKey(publicKey, privateKey);  
        // 数据解密  
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, secretKey);  

        return cipher.doFinal(data);  
    }  

    /** 
     * 构建密钥 
     * @param publicKey 
     *            公钥 
     * @param privateKey 
     *            私钥 
     * @return 
     * @throws Exception 
     */  
    private SecretKey getSecretKey(String publicKey, String privateKey) throws Exception {
        byte[] pubKeyBytes = base64Decoder.decodeBuffer(publicKey);

        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

        // 初始化私钥
        byte[] priKeyBytes = base64Decoder.decodeBuffer(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
        Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
        keyAgree.init(priKey);
        keyAgree.doPhase(pubKey, true);

        // 生成本地密钥
        SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);

        return secretKey;
    }

    /**
     * 取得私钥
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return base64Encoder.encode(key.getEncoded());
    }

    /**
     * 取得公钥
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return base64Encoder.encode(key.getEncoded());
    }
}

(2).RSA算法

  RSA同时有两把钥匙,公钥与私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。
  RSA算法包括四个步骤:密钥生成,密钥分发,加密和解密。
  RSA是一个相对较慢的算法,因此,它不太常用于直接加密用户数据。更经常地,RSA通过加密的共享密钥,用于对称密钥加密,这又可以以更高的速度执行批量加密 - 解密操作。

package demo.rsa;

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.junit.Before;
import org.junit.Test;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Demo01_RSA {
    private String message = "This is message";
    private int keySize = 512;
    public String KEY_ALGORITHM = "RSA";
    public String SIGNATURE_ALGORITHM = "MD5withRSA";
    private String PUBLIC_KEY = "RSAPublicKey";
    private String PRIVATE_KEY = "RSAPrivateKey";
    private BASE64Encoder base64Encoder = new BASE64Encoder();
    private BASE64Decoder base64Decoder = new BASE64Decoder();

    String publicKey;
    String privateKey;

    @Before
    public void ready() throws Exception {
        Map<String, Object> keyMap = initKey();
        publicKey = getPublicKey(keyMap);
        privateKey = getPrivateKey(keyMap);
        System.err.println("公钥:\r" + publicKey);
        System.err.println("私钥:\r" + privateKey);
    }

    @Test
    public void testEncryptDecrypt() throws Exception {
        System.err.println("公钥加密——私钥解密");
        System.err.println("加密前: " + message);
        byte[] data = message.getBytes();
        byte[] encodedData = encryptByPublicKey(data, publicKey);
        System.out.println("加密后的数据经Base64编码后为:" + base64Encoder.encode(encodedData));
        byte[] decodedData = decryptByPrivateKey(encodedData, privateKey);
        String outputStr = new String(decodedData);
        System.out.println("解密后: " + outputStr);
    }

    @Test
    public void testSign() throws Exception {
        System.err.println("私钥加密——公钥解密");
        byte[] data = message.getBytes();
        System.out.println("加密前: " + message);
        byte[] encodedData = encryptByPrivateKey(data, privateKey);
        System.out.println("加密后的数据经Base64编码后为:" + base64Encoder.encode(encodedData));
        byte[] decodedData = decryptByPublicKey(encodedData, publicKey);
        String outputStr = new String(decodedData);
        System.out.println("解密后: " + outputStr);
        System.err.println("私钥签名——公钥验证签名");
        // 产生签名
        String sign = sign(encodedData, privateKey);
        System.out.println("签名:\r" + sign);
        // 验证签名
        boolean status = verify(encodedData, publicKey, sign);
        System.out.println("状态:\r" + status);
    }

    /**
     * 初始化密钥
     * 
     * @return
     * @throws Exception
     */
    public Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(keySize);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 公钥
        PublicKey publicKey = keyPair.getPublic();
        // 私钥
        PrivateKey privateKey = keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 用公钥加密
     * 
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
        // 对公钥解密
        byte[] keyBytes = base64Decoder.decodeBuffer(key);
        // 取得公钥
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        return cipher.doFinal(data);
    }

    /**
     * 用私钥加密
     * 
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
        // 对密钥解密
        byte[] keyBytes = base64Decoder.decodeBuffer(key);

        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);

        return cipher.doFinal(data);
    }

    /**
     * 用私钥解密
     * 
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
        // 对密钥解密
        byte[] keyBytes = base64Decoder.decodeBuffer(key);
        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 用公钥解密
     * 
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
        // 对密钥解密
        byte[] keyBytes = base64Decoder.decodeBuffer(key);
        // 取得公钥
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);

        // 对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);

        return cipher.doFinal(data);
    }

    /**
     * 用私钥对信息生成数字签名
     * 
     * @param data
     *            加密数据
     * @param privateKey
     *            私钥
     * 
     * @return
     * @throws Exception
     */
    public String sign(byte[] data, String privateKey) throws Exception {
        // 解密由base64编码的私钥
        byte[] keyBytes = base64Decoder.decodeBuffer(privateKey);
        // 构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取私钥匙对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);
        return base64Encoder.encode(signature.sign());
    }

    /**
     * 校验数字签名
     * 
     * @param data
     *            加密数据
     * @param publicKey
     *            公钥
     * @param sign
     *            数字签名
     * @return 校验成功返回true 失败返回false
     * @throws Exception
     */
    public boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        // 解密由base64编码的公钥
        byte[] keyBytes = base64Decoder.decodeBuffer(publicKey);
        // 构造X509EncodedKeySpec对象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取公钥匙对象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        // 验证签名是否正常
        return signature.verify(base64Decoder.decodeBuffer(sign));
    }

    /**
     * 取得私钥
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return base64Encoder.encode(key.getEncoded());
    }

    /**
     * 取得公钥
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return base64Encoder.encode(key.getEncoded());
    }
}

  testEncryptDecrypt()方法的运行结果:

公钥:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMEKI7YFB4sFyNuVERUKKZ3lDU8CcasqGI65RXHak3YS
pyQ0tH0AIiDKIl0xrrqMN+x7MUVFcn3Dkk0XysBeJGkCAwEAAQ==
私钥:
MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAwQojtgUHiwXI25URFQopneUNTwJx
qyoYjrlFcdqTdhKnJDS0fQAiIMoiXTGuuow37HsxRUVyfcOSTRfKwF4kaQIDAQABAkBy+A78wVIU
woU2AjoCReMzXs/mVpZMUu39UurBPGTG1JnaCnNbeZiiKYnyDpRxU6G5EMz4Aj4zcpPvr8xM3oOB
AiEA/wwuulqdCg9tcCUq+UcnlOXXzXE6KBsJL+Jtm5tfQh0CIQDBwq3lvLh849tHXefrO54EzHw1
OtZprDusiqWX1/OZvQIgNEzMPDyenVSCjY+hm1KDBF6JfUBtu5GntaEJ9vHKe+UCIDD39aERMAMI
RxvjRMCzoX0O/Pkq9JNiXvZ9V9pnTn51AiAWI/mYGkvyA9YSaT4shuyHE/33GZ+ffC0Pw1vqfiZk
DA==
公钥加密——私钥解密
加密前: This is message
加密后的数据经Base64编码后为:eow5WTkbq6la9cJZx1D/SIjrpd+oDkttm7xGQ5WGyiBe0wvo4oDmczLAK+qGSL8VlvnUPbUrrlTR
lzmlhFPw4A==
解密后: This is message

  testEncryptDecrypt()方法的运行结果:

公钥:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJaCjaGhiwBRLAiahpFTUV/QaL6bIgMd/VBs36lgRJcJ
jCWP8jND5knYwgRglbyHsIQmjosytlH5FQ0MWzXbTicCAwEAAQ==
加密前: This is message
私钥:
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAloKNoaGLAFEsCJqGkVNRX9Bovpsi
Ax39UGzfqWBElwmMJY/yM0PmSdjCBGCVvIewhCaOizK2UfkVDQxbNdtOJwIDAQABAkAV3ZeX4LEh
9ajxAwoDuNf8lHlzH3R8gXHYqkiAAdVVH7tp7WrNdpetl5Bycg2Ofvv4oVv+Ozt26CXWW3hu5snR
AiEA4GN7mkH/rh1CO3tpsXbws7rHvXxzArYATmhKKsVdR78CIQCrtqgZl7ExQk6ShtqNP2r8eWx1
fQKbYSbC3Z5Fx0fTmQIhAMkpRoj/O/la76KlvBZFu/pUq3Z7WBwJD/M62arIbxy/AiBjMxm390d5
gqnrwpZeBWYkR22eDkNESoLfDrMR8seO+QIhAKYfO9qfueCZSQI3BF/IIvKF+fUuPFtd4ixw/Ypl
yult
私钥加密——公钥解密
加密后的数据经Base64编码后为:CfZMIuwE0LrvCpumDz/eHrxwI+R5FSqVcPeFLXO/mFtacnUa7fbYMzLIlECM+FJSQF685HJretnW
nh9fFk6YMQ==
解密后: This is message
私钥签名——公钥验证签名
签名:
hzJXB5DT8cxnqvLEMjpiYzSSozGi8w51WOQ4/okdND/i8JeUgwfC4OPLrd3D73Uyl66ki0jziz35
foZq18CE6Q==
状态:
true
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值