首页 > 新手入门 > 正文

区块链非对称加密技术【附源代码】

分类:教程满天星天团满天星天团 2019-11-14 18:38

  我们对加密技术应该不会陌生。小时候经常会在电视里看到女主收信之后,小心翼翼地把空白信纸浸在水里,一会儿信纸上就出现了男主的面基信息。

  最简陋的加密技术莫过于考试作弊了,回想一下自己有没有过考试的时候用摸鼻子,抓耳朵,揉眼睛等等方法来传递答案的经历。这种加密技术操作很简单,加密过程就三步:明文 - 秘钥(也可以叫做算法)- 密文,解密则倒过来:密文 - 秘钥 - 明文。作弊的时候,明文就是答案,秘钥是你们事先合谋好的暗语,比如摸鼻子,抓耳朵,揉眼睛分别代表答案A,B,C,密文就是摸鼻子,抓耳朵,揉眼睛的动作。对方收到你的信号时,只需要按照密谋好的暗语对应过来,当你摸鼻子的时候就知道是答案A,当你抓耳朵就知道是B。

  什么是对称加密技术?

  

这种加密技术加密和解密时用到的暗语是一样的,你把密文用加密的秘钥或者算法倒推一下,就能得出明文,我们把加密和解密对称的加密方法叫做对称加密。对称加密在加密算法中是最快速、最简单的,因为其效率很高,所以被广泛运用在加密协议中。但是它的最大问题是如果秘钥被他人知道了,或者算法被破解,密文就暴露了。考试作弊如果被老师识破,估计你只能抱鸭蛋,还要请家长了。 据说1894年6月,日本破译了清朝的电报密码,从战前高升号运兵船的行踪,到大战中的兵力调遣,到战后马关谈判期间李鸿章与朝廷的电报,中方密电在日本高层眼中毫无秘密可言,导致甲午战争我方损失惨重。密码被成功破译的另一个重要事件是二战期间,德国的密码系统被英国数学家,逻辑学家,计算机和人工智能的开荒者--艾兰图灵破解(当然也有一种说法,在图灵之前,波兰人就已经有这方面进展,提供给英国,图灵才逐步开发出密码机。),让德国人的军事机密完全暴露在盟军的眼皮地下。这一事件挽救了无数无辜者的生命,也最终让盟军赢得了二战。

  

可见,加密技术是多么的重要,稍有不慎,损失甚至可以大到一次战争,一个国家。所以,在信息传输安全至关重要的网络世界,特别是以传输价值和数字资产为主要功能的区块链,再用传统的加密技术肯定不行。这就有了我们现在知道的非对称加密技术。

  什么是非对称加密?

  

  顾名思义,非对称加密指的是加密和解密用到的密码算法是不一样的。用一套算法来加密,解密则需要用另一套算法,而且,不能从加密的算法推算出解密的算法。区块链用的加密算法就是不对称加密,它用公钥来加密,而用对应的私钥来解密。因为公钥是公开的秘钥,而私钥则是私密的,只有私钥的主人才知道,而且我们可以用私钥推导出公钥,却无法用公钥倒推出私钥,目前的技术就算用最先进的电脑计算数千年,破解私钥的可能性也很小。所以别人可以用自己的公钥对信息进行加密传输,只有私钥的主人才能破解密文,因为只有他才掌握自己的私钥。这种加密技术安全性非常高,除非私钥丢了,否则就目前的技术还没有破解的可能。

  

  非对称加密的操作过程为:

  加密:明文 - 公钥(经过算法运算) - 密文

  解密:密文 - 私钥 - 明文

  我用你的公钥加密一段信息,发到网络,如果被赵丽颖收到,她能打开吗?不能。当然了,如果你把私钥告诉她就没法了。私钥可是你区块链世界中财产的凭证和保护神,你可不能告诉任何人。

  非对称加密技术是加密货币协议中最重要的部分之一,它被用于:

  1. 加密货币钱包的创建,以确保加密货币只能由所有者使用;

  2. 交易签名(数字签名),这是加密货币协议的核心组件。

  简而言之,如果您将加密货币发送给其他人,则使用您的私钥(或使用私钥生成的密码)进行签名,旷工则会用你的公钥验证你的签名是否匹配。所以,如果黑客获得了你的私钥,他们就可以把你的加密货币发给自己。

  

  在区块链中,我们通常进行的代币发送过程所用到的地址是用公钥生成的。它与公钥的关系就似公钥与私钥的关系,公钥可以推算出地址,但是从地址却无法反推出公钥。

  由于有了这种加密技术,我们就可以在知道对方地址的情况下安全地转账,而无需用到第三方中心化的机构来为我们进行担保,或者确认真假。

  其实,非对称加密技术不仅用于区块链世界的加密货币中,我们熟知的微信支付,支付宝,手机银行等都使用了这种加密方法来确保资产信息在传输过程中不被破解。它被广泛地使用于不安全的媒体上信息的交换。

  非对称加密,让区块链的加密货币可以安全地在陌生人之间直接传输,也可以保护你传给他人的小秘密不被不相关的人识破。

区块链技术不是一个新发明的技术,而是一个集成了多方面基础技术的综合性技术系统,是几个之前就有的基础技术的优雅组合而成。我认为,其中有四项必不可缺的核心技术,分别是:共识机制、密码学原理、链式哈希结构和分布式数据存储(多节点)。

密码学原理在区块链中,信息的传播按照公钥、私钥这种非对称数字加密技术实现交易双方的互相信任。非对称加密技术是区块链技术体系很重要的一部分。

公钥私钥的原则:

一个公钥对应一个私钥,公钥私钥成对出现。

密钥对中,让大家都知道的是公钥,不告诉大家,只有自己知道的,是私钥。

公钥用来加密和验证数字签名;私钥用来解密和生成数字签名。生成数字签名本质上是用私钥进行加密,验证数字签名是用公钥对私钥加密密文进行解密。可以归纳为:如果用其中一个密钥加密数据,则只有对应的那个密钥才可以解密。 

如果用其中一个密钥可以进行解密数据,则该数据必然是对应的那个密钥进行的加密。

为什么用非对称加密?

 一个网站要对传输进行加密,如果用对称加密,则有以下几种情况:

1  每个访问用户的秘钥相同。这种情况,服务器只要保存一个秘钥。除非你的网站用户是特定的内部用户,否则用户拿到秘钥就能对密文进行解密。

2  每个访问用户的秘钥不一样。这种情况,服务器需要保存大量秘钥。如果网站有上亿用户,则需要存储上亿的秘钥,维护成本巨大。

如果是非对称加密,网站只需要保存自己的私钥,用户可以随意下载网站公钥。

数字签名:就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。数字签名了的文件的完整性是很容易验证的

 流程:

(1) 被发送文件用密码散列函数(MD5,SHA,SM3)产生的摘要

(2) 发送方用自己的私用密钥对摘要再加密,这就形成了数字签名。

(3) 将原文和加密的摘要同时传给对方。

(4) 对方用发送方的公共密钥对摘要解密,获取发送方生成的摘要,同时对收到的文件用SHA编码加密产生又一摘要。

(5) 将解密后的摘要和收到的文件在接收方重新加密产生的摘要相互对比。如两者一致,则说明传送过程中信息没有被破坏或篡改过。否则不然。

数字签名,可以保证收到的文件没有被篡改,也可以保证发送者的身份。因为私钥生产了数字签名,私钥是不公开的。

说了这么多,还是写段代码来试试看。

/**

 * 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 MapinitRsaKey() {

        //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();

            MapkeyMap = new HashMap(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) {

        MapkeyMap = 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) {

        MapkeyMap = 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 MapinitRsaKey() {

        //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();

            MapkeyMap = new HashMap(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 。  注意生成签名和验签的方法要相同。

部分内容作者简介:甜橙金融技术部技术总监,负责公司核心平台设计与开发,新技术预研与落地。10年代码和架构设计经验,之前在国内知名一线互联网公司从事核心系统的开发和设计。

”满天星天团“公众号创始人

  宿风,中国风,数字科技时代布道人

  满天星科技发展有限公司董事长

  外滩并购中心主任

上一篇:区块科普 | 区块链的一些基本技术

下一篇:区块链是什么?如何学习区块链?

猜您喜欢
关于我们联系我们作者投稿APP下载