Blockchain Technology - Asymmetric Encryption

Blockchain technology is not a newly invented technology , but a comprehensive technology system that integrates various basic technologies. It is an elegant combination of several pre-existing basic technologies . In my opinion, there are four essential core technologies, namely: consensus mechanism, cryptography principle, chain hash structure and distributed data storage (multi-node).

The principle of cryptography In the blockchain, the dissemination of information is based on the asymmetric digital encryption technology of public key and private key to achieve mutual trust between the two parties of the transaction . Asymmetric encryption technology is an important part of the blockchain technology system.

The principle of public key and private key:

  1. A public key corresponds to a private key, and the public key and the private key appear in pairs.
  2. In the key pair, what everyone knows is the public key, and what only you know is the private key.
  3. The public key is used to encrypt and verify digital signatures; the private key is used to decrypt and generate digital signatures. Generating a digital signature is essentially encrypting with the private key, and verifying the digital signature is decrypting the ciphertext encrypted by the private key with the public key. It can be summarized as follows: if one of the keys is used to encrypt data, only the corresponding key can decrypt it. 
  4. If the data can be decrypted with one of the keys, the data must be encrypted by the corresponding key.

Why use asymmetric encryption?

 A website needs to encrypt the transmission. If symmetric encryption is used, there are several situations:

1 The secret key of each access user is the same. In this case, the server only needs to store a secret key. Unless your website user is a specific internal user, the user can decrypt the ciphertext with the secret key.

2 The secret key of each access user is different. In this case, the server needs to store a large number of secret keys. If the website has hundreds of millions of users, hundreds of millions of secret keys need to be stored, and the maintenance cost is huge.

If it is asymmetric encryption, the website only needs to save its own private key, and users can download the public key of the website at will.


Digital signature: It is a digital string that can only be generated by the sender of the information and cannot be forged by others. This digital string is also an effective proof of the authenticity of the information sent by the sender of the information. The integrity of digitally signed files can be easily verified 

 process:
(1) The digest generated by the cryptographic hash function (MD5, SHA, SM3) of the sent file (2) The sender re-encrypts the digest with its own private key, which forms a digital signature.
(3) Send the original text and the encrypted abstract to the other party at the same time.
(4) The other party decrypts the digest with the sender's public key, obtains the digest generated by the sender , and encrypts the received file with SHA encoding to generate another digest .
(5) Compare the decrypted digest with the digest generated by the re-encryption of the received file at the receiver . If the two are consistent, it means that the information has not been damaged or tampered with during the transmission process. Otherwise not.

Digital signatures can ensure that the received document has not been tampered with, and can also guarantee the identity of the sender. Because the private key produces the digital signature, the private key is not public.

Having said so much, let's write a piece of code to try it out.

 
 
/**
 * Bestpay.com.cn Inc.
 * Copyright (c) 2011-2018 All Rights Reserved.
 */
package rsa;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

/**
 * 
 * @author huyajun
 * @version $Id: RSAsecurityTest.java, v 0.1 2018年3月14日 下午6:09:53 huyajun Exp $
 */
public class RSAsecurityTest {

    public static String PUBLIC_KEY  = "pub_key";
    public static String PRIVATE_KEY = "pri_key";

    /**
     * BASE64解密
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptBASE64(String key) {
        return Base64.getDecoder().decode(key);
    }

    /**
     * BASE64加密
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptBASE64(byte[] key) {
        return Base64.getEncoder().encodeToString(key);
    }

    /**
     * 初始化密钥对
     * 
     * @return
     */
    public static Map<String, String> initRsaKey() {
        //1.初始化秘钥
        KeyPairGenerator keyPairGenerator;
        try {
            //1.初始化秘钥
            keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //秘钥长度
            keyPairGenerator.initialize(512);
            //初始化秘钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //公钥
            RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            //私钥
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();

            Map<String, String> keyMap = new HashMap<String, String>(2);
            keyMap.put(PUBLIC_KEY, encryptBASE64(rsaPublicKey.getEncoded()));
            keyMap.put(PRIVATE_KEY, encryptBASE64(rsaPrivateKey.getEncoded()));
            return keyMap;

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    ///////////////////////////////////////////////////////////////////////

    /**
     * 公钥处理,返回base64编码的字符串
     * @param file
     * @param rsaPublicKeyStr
     * @param model  加密:Cipher.ENCRYPT_MODE;解密:Cipher.DECRYPT_MODE
     * @return  公钥处理后的字符串,base64编码
     */
    public static String publicKeyDeal(String file, String rsaPublicKeyStr, int model) {

        try {
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
                decryptBASE64(rsaPublicKeyStr));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(model, publicKey);
            byte[] result = cipher.doFinal(decryptBASE64(file));
            // 必须用base64 进行编解码
            return encryptBASE64(result);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;

    }

    /**
     * 私钥 处理,返回base64编码的字符串
     * @param file
     * @param rsaPrivateKeyStr
     * @param model   加密:Cipher.ENCRYPT_MODE;解密:Cipher.DECRYPT_MODE
     * @return 私钥处理后的字符串,base64编码
     */
    public static String privateKeyDeal(String file, String rsaPrivateKeyStr, int model) {
        try {
            //生成私钥

            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
                decryptBASE64(rsaPrivateKeyStr));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance("RSA");
            //初始化加密
            cipher.init(model, privateKey);
            byte[] result = cipher.doFinal(decryptBASE64(file));

            //不能返回,  new String(result) ,会出现乱码,导致没法解码
            // 必须用base64 进行编解码
            return encryptBASE64(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String EncoderByMd5(String str) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] result = md5.digest(str.getBytes("utf-8"));
            return toHex(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String toHex(byte[] bytes) {

        final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
        StringBuilder ret = new StringBuilder(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
        }
        return ret.toString();
    }

    public static void main(String[] args) {
        Map<String, String> keyMap = initRsaKey();
        String file = "甜橙金融,互联网金融行业第三";

        // 公钥加密,私钥解密,常规用法;
        if (keyMap != null) {

            String str = publicKeyDeal(encryptBASE64(file.getBytes()), keyMap.get(PUBLIC_KEY),
                Cipher.ENCRYPT_MODE);
            System.out.println("公钥对明文加密:" + str);
            String str2 = privateKeyDeal(str, keyMap.get(PRIVATE_KEY), Cipher.DECRYPT_MODE);
            System.out.println("私钥对密文解密:" + new String(decryptBASE64(str2)));

        }
        System.out.println("");

        // 私钥加密,公钥解密,得到明文;-- 数字签名 用这个道理
        if (keyMap != null) {

            String str = privateKeyDeal(encryptBASE64(file.getBytes()), keyMap.get(PRIVATE_KEY),
                Cipher.ENCRYPT_MODE);
            System.out.println("私钥对明文加密:" + str);

            String str2 = publicKeyDeal(str, keyMap.get(PUBLIC_KEY), Cipher.DECRYPT_MODE);
            System.out.println("公钥对密文解密:" + new String(decryptBASE64(str2)));

        }
        System.out.println("");
        /////////////////////////////////////////
        // 数字签名
        // 生产摘要
        String md5Str = EncoderByMd5(file);
        System.out.println("发送方生成摘要:" + md5Str);
        //2用私钥 对摘要进行加密
        String signStr = privateKeyDeal(encryptBASE64(md5Str.getBytes()), keyMap.get(PRIVATE_KEY),
            Cipher.ENCRYPT_MODE);
        System.out.println("发送方用私钥对摘要加密:" + signStr);

        //3 发送原文+摘要密文(数字签名) 给接收方
        System.out.println("接收方收到原文和摘要");

        //4 接收方用公钥解密摘要
        String md5Str_Decrypt = new String(decryptBASE64(publicKeyDeal(signStr,
            keyMap.get(PUBLIC_KEY), Cipher.DECRYPT_MODE)));
        System.out.println("接收方用公钥对摘要解密:" + md5Str_Decrypt);

        //5 用同样的算法对原文生成摘要
        String md5Str2 = EncoderByMd5(file);
        System.out.println("接收方生成的摘要:" + md5Str2);

        //6 对比公钥解密的摘要和用原文生成的摘要
        if (md5Str2.equals(md5Str_Decrypt)) {
            System.out.println("数字签名验证成功!");
        } else {
            System.out.println("数字签名验证失败!");
        }
    }
}

代码可以看出来:用公钥加密后,用对应的私钥可以解密;反过来,私钥加密后,用对应的公钥也可以解密。

整个数字签名的流程稍微有点复杂。Java 还提供了专门的签名类,省去了这些繁琐的步骤。

代码如下:

/**
 * Bestpay.com.cn Inc.
 * Copyright (c) 2011-2018 All Rights Reserved.
 */
package rsa;

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.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author huyajun
 * @version $Id: SignTest.java, v 0.1 2018年4月22日 下午7:29:08 huyajun Exp $
 */
public class SignTest {

    private static final String PUBLIC_KEY  = "PUBLIC_KEY";
    private static final String PRIVATE_KEY = "PRIVATE_KEY";

    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        Map<String, String> keyMap = initRsaKey();
        String content = "123";
        String sign = null;
        try {
            sign = sign(content.getBytes(), keyMap.get(PRIVATE_KEY));
            System.out.println("私钥签名结果:" + sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //5.公钥验签
        try {
            boolean flag = verify(content.getBytes(), keyMap.get(PUBLIC_KEY), sign);
            System.out.println("公钥验证数字签名:" + flag);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    ////////////////////////////////////////////////////////
    /**
     * BASE64解密
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptBASE64(String key) {
        return Base64.getDecoder().decode(key);
    }

    /**
     * BASE64加密
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptBASE64(byte[] key) {
        return Base64.getEncoder().encodeToString(key);
    }

    /**
     * 初始化密钥对
     * 
     * @return
     */
    public static Map<String, String> initRsaKey() {
        //1.初始化秘钥
        KeyPairGenerator keyPairGenerator;
        try {
            //1.初始化秘钥
            keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //秘钥长度
            keyPairGenerator.initialize(512);
            //初始化秘钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //公钥
            RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            //私钥
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();

            Map<String, String> keyMap = new HashMap<String, String>(2);
            keyMap.put(PUBLIC_KEY, encryptBASE64(rsaPublicKey.getEncoded()));
            keyMap.put(PRIVATE_KEY, encryptBASE64(rsaPrivateKey.getEncoded()));
            return keyMap;

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥签名
     * 
     * @param data   原文件
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        //构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decryptBASE64(privateKey));
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //取私钥匙对象
        PrivateKey privateKeyObj = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //用私钥对信息生成数字签名
        Signature signature = Signature.getInstance("MD5withRSA");// MD2withRSA  SHA1withRSA  MD5withRSA
        signature.initSign(privateKeyObj);
        signature.update(data);

        return encryptBASE64(signature.sign());
    }

    /**
     * 校验数字签名
     * @param data  加密数据
     * @param publicKey 公钥
     * @param sign  数字签名
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        //构造X509EncodedKeySpec对象
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decryptBASE64(publicKey));
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //生成公钥匙对象
        PublicKey publicKeyObj = keyFactory.generatePublic(x509EncodedKeySpec);

        Signature signature = Signature.getInstance("MD5withRSA");// MD2withRSA  SHA1withRSA  MD5withRSA
        signature.initVerify(publicKeyObj);
        signature.update(data);
        //验证签名是否正常
        return signature.verify(decryptBASE64(sign));

    }
}

Signature 类帮我们实现了生成数字签名和校验数字签名的方法,直接用,比自己去实现方便的多。

Signature.getInstance("MD5withRSA");   表示用MD5做摘要,用RSA做加解密。同理你还可以选择SHA1withRSA 。  注意生成签名和验签的方法要相同。


今天就讲到这里,下一篇文章,我们介绍区块链知识点之--共识算法。敬请期待



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324707074&siteId=291194637