加密之非对称RSA,DH,DSA,ECC

1 RSA算法

1.1 定义

RSA,这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
这种加密算法的特点主要是密钥的变化,DES只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。但是RSA同时有两把钥匙,公钥私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。

RSA算法一直是最广为使用的非对称加密算法。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。

1.2 原理

流程分析:
甲方构建密钥对儿,将公钥公布给乙方,将私钥保留。
甲方使用私钥加密数据,然后用私钥对加密后的数据签名,发送给乙方签名以及加密后的数据;乙方使用公钥、签名来验证待解密数据是否有效,如果有效使用公钥对数据解密。
乙方使用公钥加密数据,向甲方发送经过加密后的数据;甲方获得加密数据,通过私钥解密

1.2.1 互质关系

如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系(coprime)。比如,15和32没有公因子,所以它们是互质关系。这说明,不是质数也可以构成互质关系。
关于互质关系,不难得到以下结论:

  1. 任意两个质数构成互质关系,比如13和61。
  2. 一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如3和10。
  3. 如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和57。
  4. 1和任意一个自然数是都是互质关系,比如1和99。
  5. p是大于1的整数,则p和p-1构成互质关系,比如57和56。
  6. p是大于1的奇数,则p和p-2构成互质关系,比如17和15

1.2.2 欧拉函数

请思考以下问题:
任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?(比如,在1到8之中,有多少个数与8构成互质关系?)
计算这个值的方法就叫做欧拉函数,以φ(n)表示。在18之中,与8形成互质关系的是1、3、5、7,所以 φ(n) = 4
φ(n) 的计算方法并不复杂,但是为了得到最后那个公式,需要一步步讨论。
第一种情况
如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。
第二种情况
如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如5与1、2、3、4都构成互质关系。
第三种情况
如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),则
在这里插入图片描述
比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4
这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p^(k-1)个,即1×p、2×p、3×p、...、p^(k-1)×p,把它们去除,剩下的就是与n互质的数。
上面的式子还可以写成下面的形式:
在这里插入图片描述
可以看出,上面的第二种情况是 k=1 时的特例。
第四种情况
如果n可以分解成两个互质的整数之积,n = p1 × p2
则 φ(n) = φ(p1p2) = φ(p1)φ(p2)
积的欧拉函数等于各个因子的欧拉函数之积。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24
这一条的证明要用到"中国剩余定理",这里就不展开了,只简单说一下思路:如果ap1互质(a<p1),b与p2互质(b<p2),c与p1p2互质(c<p1p2),则c与数对 (a,b) 是一一对应关系。由于a的值有φ(p1)种可能,b的值有φ(p2)种可能,则数对 (a,b) 有φ(p1)φ(p2)种可能,而c的值有φ(p1p2)种可能,所以φ(p1p2)就等于φ(p1)φ(p2)。
第五种情况
因为任意一个大于1的正整数,都可以写成一系列质数的积。
在这里插入图片描述
根据第4条的结论,得到
在这里插入图片描述
再根据第3条的结论,得到
在这里插入图片描述
也就等于
在这里插入图片描述
这就是欧拉函数的通用计算公式。比如,1323的欧拉函数,计算过程如下:
在这里插入图片描述

1.2.3 欧拉定理

欧拉函数的用处,在于欧拉定理。欧拉定理指的是:
如果两个正整数an互质,则n的欧拉函数 φ(n) 可以让下面的等式成立:
在这里插入图片描述
也就是说,aφ(n)次方被n除的余数为1。或者说,aφ(n)次方减去1,可以被n整除。比如,3和7互质,而7的欧拉函数φ(7)等于6,所以3的6次方(729)减去1,可以被7整除(728/7=104)。
欧拉定理的证明比较复杂,这里就省略了。我们只要记住它的结论就行了。
欧拉定理可以大大简化某些运算。比如,7和10互质,根据欧拉定理,
在这里插入图片描述
已知 φ(10) 等于4,所以马上得到74倍数次方的个位数肯定是1。
在这里插入图片描述
因此,7的任意次方的个位数(例如7的222次方),心算就可以算出来。
欧拉定理有一个特殊情况。
假设正整数a与质数p互质,因为质数pφ(p)等于p-1,则欧拉定理可以写成
在这里插入图片描述
这就是著名的费马小定理。它是欧拉定理的特例。
欧拉定理是RSA算法的核心。理解了这个定理,就可以理解RSA。

1.2.4 模反元素

还剩下最后一个概念:
如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1。
在这里插入图片描述
这时,b就叫做a的模反元素
比如,3和11互质,那么3的模反元素就是4,因为 (3 × 4)-1 可以被11整除。显然,模反元素不止一个, 4加减11的整数倍都是3的模反元素 {...,-18,-7,4,15,26,...},即如果b是a的模反元素,则 b+kn 都是a的模反元素。
欧拉定理可以用来证明模反元素必然存在。

可以看到,a的 φ(n)-1 次方,就是a的模反元素。

1.3 实际操作

下面代码用到的常量:

    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";

1.3.1 生成公私钥并获取

   /**
     * 初始化密钥
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);

        KeyPair keyPair = keyPairGen.generateKeyPair();
        //   公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //   私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

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

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

1.3.2 公私钥加签验签

    /**
     * 用私钥对信息生成数字签名
     * @param data       加密数据
     * @param privateKey 私钥
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        //   解密由base64编码的私钥
        byte[] keyBytes = de.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 en.encode(signature.sign());
    }


    /**
     * 公钥校验数字签名
     * @param data      加密数据
     * @param publicKey 公钥
     * @param sign      数字签名
     * @return 校验成功返回true   失败返回false
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        //   解密由base64编码的公钥
        byte[] keyBytes = de.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(de.decodeBuffer(sign));
    }

1.3.3 公私钥加密解密

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

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

    /**
     * 加密<br>
     * 用公钥加密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(byte[] data, String key) throws Exception {
        //   对公钥解密
        byte[] keyBytes = de.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 en.encode(cipher.doFinal(data));
    }

    /**
     * 加密<br>
     * 用私钥加密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPrivateKey(byte[] data, String key) throws Exception {
        //   对密钥解密
        byte[] keyBytes = de.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 en.encode(cipher.doFinal(data));
    }

1.3.4 结果验证

    public static void main(String[] args) throws Exception{
        //生成 公钥私钥
        Map<String, Object> keyMap = initKey();
        String publicKey = getPublicKey(keyMap);
        String privateKey = getPrivateKey(keyMap);
        System.err.println("公钥:   \n\r" + publicKey);
        System.err.println("私钥:   \n\r" + privateKey);

        System.err.println("公钥加密——私钥解密");
        String inputStr = "abc";
        byte[] data = inputStr.getBytes();
        String encodedData = encryptByPublicKey(data, publicKey);
        String decodedData = decryptByPrivateKey(de.decodeBuffer(encodedData),privateKey);
        System.err.println("加密前:   " + inputStr +"\n\r" +"加密中:   "+encodedData+"\n\r" + "解密后:   " + decodedData);
        System.err.println("公钥加密——私钥解密");


        System.err.println("私钥加密——公钥解密");
        String inputStr_new = "sign";
        byte[] data_new = inputStr_new.getBytes();

        String encodedData_new = encryptByPrivateKey(data_new, privateKey);
        String decodedData_new =decryptByPublicKey(de.decodeBuffer(encodedData_new), publicKey);

        System.err.println("加密前:   " + inputStr_new + "\n\r" +"加密中:   "+encodedData_new+"\n\r" + "解密后:   " + decodedData_new);
        System.err.println("私钥加密——公钥解密");

        System.err.println("私钥签名——公钥验证签名");
        //   产生签名
        String sign = sign(encodedData_new.getBytes(), privateKey);
        System.err.println("签名:\r\n" + sign);
        //   验证签名
        boolean status = verify(encodedData_new.getBytes(), publicKey, sign);
        System.err.println("状态:\r\n" + status);
        System.err.println("私钥签名——公钥验证签名");
    }

2 DH算法

2.1 定义

接下来我们分析DH加密算法,一种适基于密钥一致协议的加密算法。
Diffie-Hellman算法(D-H算法),密钥一致协议,非对称加密算法。
是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成一致的、可以共享的密钥。换句话说,就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!该算法源于中国的同余定理——中国馀数定理

流程分析:

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

2.2 实际操作

下面代码用到的常量

 /**
     * 默认密钥字节数
     * Default Keysize 1024
     */
    private static final int KEY_SIZE = 1024;
    public static final String ALGORITHM = "DH";
    /**
     * DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法。
     */
    public static final String SECRET_ALGORITHM = "DES";
    private static final String PUBLIC_KEY = "DHPublicKey";
    private static final String PRIVATE_KEY = "DHPrivateKey";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();

2.2.1 生成甲乙公私钥

2.2.1.1 生成甲公私钥

    /**
     * 初始化甲方密钥
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 甲方公钥
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        // 甲方私钥
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

2.2.1.2 根据甲公钥生成乙公私钥

    /**
     * 使用甲方公钥====初始化乙方密钥
     */
    public static Map<String, Object> initKey(String key) throws Exception {
        // 解析甲方公钥  
        byte[] keyBytes = de.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();
        // 乙方公钥
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        // 乙方私钥
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

2.2.1.3 获取公私钥

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

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

2.2.2 根据公私钥生成本地密钥

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

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

        // 初始化私钥
        byte[] priKeyBytes = de.decodeBuffer(privateKey);

        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
        Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
        //用自己的私钥初始化keyAgreement
        keyAgree.init(priKey);
        keyAgree.doPhase(pubKey, true);

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

2.2.3 加密解密

    /**
     * 加密<br>
     * @param data       待加密数据
     * @param publicKey  甲方公钥
     * @param privateKey 乙方私钥
     */
    public static String 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 en.encode(cipher.doFinal(data));
    }

    /**
     * 解密<br>
     *
     * @param data       待解密数据
     * @param publicKey  乙方公钥
     * @param privateKey 乙方私钥
     */
    public static String 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 new String(cipher.doFinal(data));
    }

2.2.4 结果验证

    public static void main(String[] args)throws Exception {
        // 生成甲方密钥对儿
        Map<String, Object> aKeyMap = initKey();
        String aPublicKey = getPublicKey(aKeyMap);
        String aPrivateKey = getPrivateKey(aKeyMap);
        System.err.println("甲方公钥:\r\n" + aPublicKey);
        System.err.println("甲方私钥:\r\n" + aPrivateKey);

        // 由甲方公钥产生本地密钥对儿
        Map<String, Object> bKeyMap = initKey(aPublicKey);
        String bPublicKey = getPublicKey(bKeyMap);
        String bPrivateKey = getPrivateKey(bKeyMap);

        System.err.println("乙方公钥:\r\n" + bPublicKey);
        System.err.println("乙方私钥:\r\n" + bPrivateKey);

        String aInput = "abc ";
        System.err.println("原文: " + aInput);

        // 由甲方公钥,乙方私钥构建密文
        String aCode = encrypt(aInput.getBytes(), aPublicKey,bPrivateKey);
        System.err.println("由甲方公钥,乙方私钥构建密文: \r\n" + aCode);
        // 由乙方公钥,甲方私钥解密
        String aDecode = decrypt(de.decodeBuffer(aCode), bPublicKey, aPrivateKey);
        System.err.println("由乙方公钥,甲方私钥解密: \r\n" + aDecode);


        System.err.println(" ===============反过来加密解密================== ");
        String bInput = "def ";
        System.err.println("原文: " + bInput);

        // 由乙方公钥,甲方私钥构建密文
        String bCode = encrypt(bInput.getBytes(), bPublicKey,aPrivateKey);
        System.err.println("由乙方公钥,甲方私钥构建密文: \r\n" + bCode);
        // 由甲方公钥,乙方私钥解密
        String bDecode = decrypt(de.decodeBuffer(bCode), aPublicKey, bPrivateKey);
        System.err.println("由甲方公钥,乙方私钥解密: \r\n" + bDecode);

    }

3 DSA算法

3.1 定义

接下来我们介绍DSA数字签名,非对称加密的另一种实现。
DSADSA-Digital Signature AlgorithmSchnorrElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。数字签名,是单向加密的升级

3.2 实际操作

用到的常量

public static final String ALGORITHM = "DSA";

    /**
     * 默认密钥字节数
     */
    private static final int KEY_SIZE = 1024;

    /**
     * 默认种子
     */
    private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";

    private static final String PUBLIC_KEY = "DSAPublicKey";
    private static final String PRIVATE_KEY = "DSAPrivateKey";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();

3.2.1 生成公私钥并获取

 /**
     * 生成密钥
     *
     * @param seed 种子
     * @return 密钥对象
     * @throws Exception
     */
    public static Map<String, Object> initKey(String seed) throws Exception {
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
        //  初始化随机产生器
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.setSeed(seed.getBytes());
        keygen.initialize(KEY_SIZE, secureRandom);

        KeyPair keys = keygen.genKeyPair();
        DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
        DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();

        Map<String, Object> map = new HashMap<String, Object>(2);
        map.put(PUBLIC_KEY, publicKey);
        map.put(PRIVATE_KEY, privateKey);
        return map;
    }

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

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

3.2.2 加签验签

3.2.2.1 私钥加签

    /**
     * 用私钥对信息生成数字签名
     * @param data       加密数据
     * @param privateKey 私钥
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        //  解密由base64编码的私钥
        byte[] keyBytes = de.decodeBuffer(privateKey);

        //  构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

        //  KEY_ALGORITHM  指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

        //  取私钥匙对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        //  用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
        signature.initSign(priKey);
        signature.update(data);
        return en.encode(signature.sign());
    }

3.2.2.1 公钥验签

    /**
     * 校验数字签名
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        //  解密由base64编码的公钥
        byte[] keyBytes = de.decodeBuffer(publicKey);

        //  构造X509EncodedKeySpec对象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        //  ALGORITHM  指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        //  取公钥匙对象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
        signature.initVerify(pubKey);
        signature.update(data);
        //  验证签名是否正常
        return signature.verify(de.decodeBuffer(sign));
    }

3.2.3 结果验证

    public static void main(String[] args) throws Exception{
        String inputStr = "abc";
        byte[] data = inputStr.getBytes();
        //  构建密钥
        Map<String, Object> keyMap = initKey(DEFAULT_SEED);

        //  获得密钥
        String publicKey = getPublicKey(keyMap);
        String privateKey = getPrivateKey(keyMap);

        System.err.println("公钥:\r\n" + publicKey);
        System.err.println("私钥:\r\n" + privateKey);

        //  产生签名    
        String sign = sign(data, privateKey);
        System.err.println("签名:\r\n" + sign);

        //  验证签名
        boolean status = verify(data, publicKey, sign);
        System.err.println("状态:\r\n" + status);
    }

4 ECC

4.1 定义

ECC非对称加密算法最高级
ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制。在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生。
ECC算法在jdk1.5后加入支持,目前仅仅只能完成密钥的生成与解析。

如果想要获得ECC算法实现,需要调用硬件完成加密/解密( ECC算法相当耗费资源 ,如果单纯使用CPU进行加密/解密,效率低下)

4.2 实际操作

注意Chipher不支持EC算法 ,以下代码仅供参考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。为了确保程序能够正常执行,使用了NullCipher类,验证程序
其中的KeyFactory.getInstance("EC", "SunEC");是参考ECKeyFactory源码

如下代码用到的常量

    public static final String ALGORITHM = "EC";
    private static final String PUBLIC_KEY = "ECCPublicKey";
    private static final String PRIVATE_KEY = "ECCPrivateKey";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();

4.2.1 生成公私钥并获取

 /**
     * 初始化密钥
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {
        BigInteger x1 = new BigInteger("2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16);
        BigInteger x2 = new BigInteger("289070fb05d38ff58321f2e800536d538ccdaa3d9", 16);

        ECPoint g = new ECPoint(x1, x2);
        //   the   order   of   generator
        BigInteger n = new BigInteger("5846006549323611672814741753598448348329118574063", 10);
        //   the   cofactor
        int h = 2;
        int m = 163;
        int[] ks = {7, 6, 3};
        ECFieldF2m ecField = new ECFieldF2m(m, ks);
        //   y^2+xy=x^3+x^2+1
        BigInteger a = new BigInteger("1", 2);
        BigInteger b = new BigInteger("1", 2);

        EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);

        ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g, n, h);
        //   公钥
        ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);

        BigInteger s = new BigInteger("1234006549323611672814741753598448348329118574063", 10);
        //   私钥
        ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);

        return keyMap;
    }

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

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

4.2.2 加密解密

    /**
     * 解密<br>
     * 用私钥解密
     */
    public static String decrypt(byte[] data, String key) throws Exception {
        //   对密钥解密
        byte[] keyBytes = de.decodeBuffer(key);
        //   取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "SunEC");
        ECPrivateKey priKey = (ECPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);

        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(), priKey.getParams());

        //   对数据解密
        //  Chipher不支持EC算法   未能实现
        Cipher cipher = new NullCipher();
        //   Cipher.getInstance(ALGORITHM,   keyFactory.getProvider());
        cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
        return new String(cipher.doFinal(data));
    }

    /**
     * 加密<br>
     * 用公钥加密
     */
    public static String encrypt(byte[] data, String privateKey) throws Exception {
        //   对公钥解密
        byte[] keyBytes = de.decodeBuffer(privateKey);

        //   取得公钥
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "SunEC");

        ECPublicKey pubKey = (ECPublicKey) keyFactory.generatePublic(x509KeySpec);

        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(), pubKey.getParams());

        //   对数据加密
        //   TODO   Chipher不支持EC算法   未能实现
        Cipher cipher = new NullCipher();
        //   Cipher.getInstance(ALGORITHM,   keyFactory.getProvider());
        cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());

        return en.encode(cipher.doFinal(data));
    }

4.2.3 结果验证

    public static void main(String[] args) throws Exception{
        String inputStr = "abc";
        byte[] data = inputStr.getBytes();

        Map<String, Object> keyMap = initKey();

        String publicKey = getPublicKey(keyMap);
        String privateKey = getPrivateKey(keyMap);
        System.err.println("公钥:    \r\n" + publicKey);
        System.err.println("私钥:   \r\n" + privateKey);

        String encodedData = encrypt(data, publicKey);

        String decodedData = decrypt(de.decodeBuffer(encodedData), privateKey);

        System.err.println("加密前:    " + inputStr + "\n\n" + "解密后:    " + decodedData);
    }
posted @ 2022-01-12 16:49  上善若泪  阅读(622)  评论(0编辑  收藏  举报