公钥私钥与证书 —— 相关内容分析与实践

Posted 河边小咸鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了公钥私钥与证书 —— 相关内容分析与实践相关的知识,希望对你有一定的参考价值。

  • 这是我在实习中碰到的一些内容,因为之前对这一块内容一窍不通,所以就抽了几天学习了一下,不得不说收获颇丰。
  • 借着这次的学习,对加密解密这方面的概念有了一定的认识,对信息传输安全相关的内容算是打了个基础,正好之前我也有做网络编程,感觉是可以尝试加入一下加密策略,不让报文裸奔了。然后就是借着证书了解了一下http/https,算是对网络有了一点新的个人理解吧。
  • 受组里的影响,我现在所有的总结都是在有道云笔记上进行的,但是说实话上面搜索有点操蛋另外markdown不让传图,由此这种长篇大论还是适合发在CSDN上。所以我完善了一下,总结出了这一篇内容。(但是不得不说有道云记小东西是真的方便,生产力upup )

一、概念相关

1. 一些关键词

  • 密钥对 在非对称加密技术中,有两种密钥,分为私钥和公钥,私钥是密钥对所有者持有,不可公布,公钥是密钥对持有者公布给他人的。
  • 公钥 公钥用来给数据加密,用公钥加密的数据只能使用私钥解密。
  • 私钥 如上,用来解密公钥加密的数据。
  • 摘要 对需要传输的文本,做一个HASH计算,一般采用SHA1,SHA2来获得。
  • 签名 使用私钥对需要传输的文本的摘要进行加密,得到的密文即被称为该次传输过程的签名。

2. 公私钥

2.1 公私钥的优点

  在对称加密中,加密和解密用的是同一个密钥。所以一旦密钥泄露,数据传输可能就会面临风险,由此非对称加密应运而生。

  在非对称加密中,存在一个密钥对,即公钥和私钥,两者只能解密对方加密的内容。在私钥不被泄露的前提下对公钥进行散发,即使在散发的过程中公钥泄露也无法解密公钥端加密的数据,相比对称式加密更加安全。

2.2 公私钥的定义

  • 公钥:是公布出去给别人用的,可以被很多人获取。用来加密和验签
  • 私钥:只能自己持有,并且不可以被其他人知道。用来解密和签名

2.3 公私钥的作用

  • 公钥加密的数据私钥可以解密
  • 私钥加密的内容公钥可以解密

2.4 公私钥的关系

  • 一个公钥对应一个私钥
  • 密钥对中,让大家都知道的是公钥,自己知道是私钥
  • 如果用其中一个密钥加密数据,则只有对应的那个密钥才能解密
  • 如果用其中一个密钥可以进行解密数据,则该数据必然由对应密钥进行加密

2.5 如何确保数据安全传输

  要达到数据安全传输的目的,必须发送方和接收方都持有对方的公钥和自己私钥,即任意一方持有自己的私钥和对方的公钥。

  则为了数据的安全性,B需要使用A的公钥来给数据加密,这样只有A的私钥才可以解密数据,这样保证了数据的安全性,这是数字签名。

  而为了保证数据发送方的真实性,B需要使用自己的私钥来数据加密,这样其他人用B的公钥进行解密,由于只有B私钥加密的数据,B的公钥才能解密,所以他人用B的公钥若能解密此数据,则证实数据是由B发送的,若不能解密,则证明数据不是用B的私钥加密,即不是B发送的,这样保证了发送的真实性。

3. 证书

3.1 证书是什么

  • 证书可以为公钥做认证,从而可以验证公钥是否真实可信,防止他人伪造公钥

3.2 何时会用到证书

  很明显,在一些情况下,不存在像上文中说的那种双方均持有完善密钥对的条件,比如说访问网页。

  在申请访问网页时,本机是不存在网页端的公钥的,由此网页端在收到访问申请时,需要把公钥发送给本机。在本机接收到公钥后,便可以通过公钥与网页端进行较为安全的数据传输。但是,在网页端把公钥发送给本机的过程中,公钥可能被他人篡改(比如被替换成另一个公钥),这样在使用此公钥数据传输时就有可能被解密,从而发生泄露(比如在网页中输入的密码之类的)。此时可以确认公钥是否被篡改的证书就出现了。

3.3 证书的作用机制

  证书其实可以看作是加密后的公钥信息,其中有公钥的摘要(所以在生成证书的时候可以指定hash加密算法),可以在发送公钥时带着证书一起发送。在本机接收到内容后,对证书进行解密即可验证随着证书一起发送的公钥是否被篡改,由此即可保证公钥的安全性。

3.4 CA机构

  如果按上面说的那个证书流程走,就会出现一个问题:谁来给公钥加密成证书?如何解密证书?此时CA机构出现了。

  CA机构即证书颁发机构,它会用自己的私钥对用户的公钥和相关信息进行加密,生成"数字证书",然后证书中心会公布自己的公钥给所有人,用来让用户使用此公钥验证"数字证书"是否由CA颁发,即是否真实可信。

  • 那么CA的公钥怎么获取?就目前来说主流浏览器中均内置了CA的公钥,所以这个问题是不用担心的。
  • 如何确保CA的公钥没有被篡改?这套信任链的起点是CA的根证书,其中存有CA的公钥。所以如果你接受了这个根证书,则代表对这个机构的信任,则所有此机构的证书均可通过该根证书来验证。所以只需要确保根证书是真的即可。

3.5 CA证书与自签名证书

  CA证书是通过验证的、可以信任的,但是在CA认证过程花费不小。所以在一般的测试或者一些情况下,可以使用自签名证书。

  自签名证书相当于自己给自己发证书,所以没有办法被验证(除非你在浏览器里安装了相应的根证书),由此浏览器可能会提示不安全。之前12306就没有获取CA证书,所以它推荐安装自己的根证书来避免被提示不安全以及确保传输安全,不过后来还是搞了CA的证书(如下图)。

3.6 https协议工作流程

  我感觉https协议工作流程可以很好的展示公私钥与证书之间的关系,由此记录一下。

客户端 流向 服务端备注
请求https连接 >> 
 << 返回证书公钥
使用CA的公钥验证
看证书是否有效
 × 如果出现问题
提示不安全
根据CA的验证结果
看公钥是否安全
 × 如果出现问题
提示不安全
如果上面验证都正常
则生成一个对称密钥
 × 这个密钥作用在
之后的密文通信
使用服务端的公钥
加密上一步生成的密钥
把加密结果发送给服务端
 >> 
 × 收到加密后的密钥
使用私钥进行解密
得到对称密钥
 << 使用对称密钥加密信息
发送给客户端
使用对称密钥解密信息
得到信息
 × 
使用对称密钥加密信息
发送给服务端
 >> 
 × 使用对称密钥解密信息
得到信息
通过对称密钥加密
的密文通道

二、openssl工具

1. openssl genrsa – 生成私钥

用于生成RSA私钥,不会生成公钥,因为公钥提取自私钥。

openssl genrsa [-out filename] [-passout arg] [-des] [-des3] [-idea] [numbits]

  • -out filename :将生成的私钥保存至filename文件,若未指定输出文件,则为标准输出。
  • numbits :指定要生成的私钥的长度,默认为1024。该项必须为命令行的最后一项参数。
  • -des :生成的密钥使用des方式进行加密。
  • -des3 :生成的密钥使用des3方式进行加密。
  • -passout args :加密私钥文件时,传递密码的格式,如果要加密私钥文件时单未指定该项,则提示输入密码。传递密码的args的格式,可从密码、环境变量、文件、终端等输入。
    a. pass:password :password表示传递的明文密码
    b. env:var :从环境变量var获取密码值
    c. file:filename :filename文件中的第一行为要传递的密码。若filename同时传递给"-passin"和"-passout"选项,则filename的第一行为"-passin"的值,第二行为"-passout"的值
    d. stdin :从标准输入中获取要传递的密码

2. openssl req – 证书相关

生成证书请求文件、验证证书请求文件和创建根CA。

openssl req
[-new] [-newkey rsa:bits] [-verify] [-x509] [-in filename] [-out filename]
[-key filename] [-passin arg] [-passout arg] [-keyout filename] [-pubkey]
[-nodes] [-[dgst]] [-config filename] [-subj arg] [-days n] [-set_serial n]
[-extensions section] [-reqexts section] [-utf8] [-nameopt] [-reqopt]
[-subject] [-subj arg] [-text] [-noout] [-batch] [-verbose]

简单说明几个参数:

  • -new :创建一个证书请求文件,会交互式提醒输入一些信息,这些交互选项以及交互选项信息的长度值以及其他一些扩展属性在配置文件(默认为openssl.cnf,还有些辅助配置文件)中指定了默认值。如果没有指定"-key"选项,则会自动生成一个RSA私钥,该私钥的生成位置也在openssl.cnf中指定了。如果指定了-x509选项,则表示创建的是自签署证书文件,而非证书请求文件

  • -newkey args :类似于"-new"选项,创建一个新的证书请求,并创建私钥。args的格式是"rsa:bits"(其他加密算法请查看man),其中bits是rsa密钥的长度,如果bits省略了(即-newkeyrsa),则长度根据配置文件中default_bits指令的值作为默认长度,默认该值为2048.如果指定了-x509选项,则表示创建的是自签署证书文件,而非证书请求文件

  • -nodes :默认情况下,openssl req自动创建私钥时都要求加密并提示输入加密密码,指定该选项后则禁止对私钥文件加密

  • -key filename :指定私钥的输入文件,创建证书请求时需要

  • -keyout filename :指定自动创建私钥时私钥的存放位置,若未指定该选项,则使用配置文件中default_keyfile指定的值,默认该值为privkey.pem

  • -x509 :指定该选项时,将生成一个自签署证书,而不是创建证书请求。一般用于测试或者为根CA创建自签名证书

  • -days n :指定自签名证书的有效期限,默认30天,需要和"-x509"一起使用。

  • -[dgst] :指定对创建请求时提供的申请者信息进行数字签名时的单向加密算法,如-md5/-sha1/-sha512等.

  • -out filename :证书请求或自签署证书的输出文件,也可以是其他内容的输出文件,不指定时默认stdout

  • -text :以文本格式打印证书请求

  • -noout :不输出部分信息 -subject :输出证书请求文件中的subject(如果指定了x509,则打印证书中的subject)

  • -pubkey :输出证书请求文件中的公钥

3. openssl rsa – 提取公钥

Rsa命令用于处理RSA密钥、格式转换和打印信息。

openssl rsa
[-inform PEM|NET|DER] [-outform PEM|NET|DER] [-in filename] [-passin arg]
[-out filename] [-passout arg] [-sgckey]
[-text] [-noout] [-modulus] [-check] [-pubin] [-pubout]
[-engine id] [-des] [-des3] [-idea]

简单说明几个例子:

从私钥中提取公钥

openssl rsa -in private.key -pubout -out public.key

查看公钥信息

openssl rsa -pubin -in pubkey.key -text

查看私钥信息

openssl rsa -in prikey.key -passin pass:"123456" -text

4. openssl rsautl – 加解密

rsautl命令用于依据公私钥加解密数据。

下面是几个示例:

公钥进行加密 (被加密文件为testFile 加密后文件为temp)

openssl rsautl -encrypt -in testFile -inkey pub_test.key -pubin -out temp   

私钥进行解密 (被解密文件为temp 解密后文件为newFile)

openssl rsautl -decrypt -in temp -inkey pri_test.key -out newFile   

私钥进行加密(签名) (被加密文件为testFile 加密后文件为temp)

openssl rsautl -sign -in testFile -inkey pri_test.key -out temp 

公钥进行解密 (被解密文件为temp 解密后文件为newFile)

openssl rsautl -verify -in temp -inkey pub_test.key -pubin -out newFile 

5. openssl示例 – 公私钥、证书与加密

下面是一组示例。

使用des3加密生成一个叫做pri_test.key的私钥,长度为2048

openssl genrsa -des3 -out pri_test.key 2048 

从私钥中提取出公钥,公钥取名为pub_test.key

openssl rsa -in pri_test.key -pubout -out pub_test.key  

生成一个叫做req1.csr的证书请求,规定证书签名算法为sha256

openssl req -new -key pri_test.key -out req1.csr -sha256    

查看证书请求req1.csr的信息

openssl req -in req1.csr -text 

验证证书请求是否被修改

openssl req -verify -in req1.csr    

依据证书请求文件创建自签名证书CA1.pem,有效期365天

openssl req -x509 -key pri_test.key -in req1.csr -out CA1.pem -days 365

当然也可以直接通过私钥创建自签名证书CA2.pem

openssl req -new -x509 -key pri_test.key -out CA2.pem -days 365

由此公私钥、自签名证书创建完毕,下面是加密相关。

创建测试文件

vim testFile 
Hi~
This 1s @ test 文件!

公钥进行加密 (被加密文件为testFile 加密后文件为temp)

openssl rsautl -encrypt -in testFile -inkey pub_test.key -pubin -out temp   

私钥进行解密 (被解密文件为temp 解密后文件为newFile)

openssl rsautl -decrypt -in temp -inkey pri_test.key -out newFile   

私钥进行加密(签名) (被加密文件为testFile 加密后文件为temp)

openssl rsautl -sign -in testFile -inkey pri_test.key -out temp 

公钥进行解密 (被解密文件为temp 解密后文件为newFile)

openssl rsautl -verify -in temp -inkey pub_test.key -pubin -out newFile 

三、在C语言中使用openssl库

1. 前期准备

首先得确保当前环境下存在openssl库。官网下载地址:https://www.openssl.org/source/

具体步骤可以去网上搜一下。大致就是下载后解压,然后./configmake命令进行配置与编译,最后make install安装一下就好了。安装好以后就可以使用openssl的库文件了。

2. 程序实例 – 公私钥加密解密

这个例子来自于我在网上找到的代码,我对它进行了一些注释和改动,主要目的是为了看一下openssl在C语言中相关的接口函数。

注意∶编译时应加上参数-lcrypto来连接相关的动态库

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<openssl/rsa.h>
#include<openssl/pem.h>
#include<openssl/err.h>
#define OPENSSLKEY "pri_test.key"//私钥
#define PUBLICKEY "pub_test.key"//公钥
#define BUFFSIZE 1024
char* my_encrypt(char *str,char *path_key);//加密
char* my_decrypt(char *str,char *path_key);//解密
int main(void){
    char *source="Hi~ \\nThis 1s @ test 文件";//要加密的文件
    char *ptr_en,*ptr_de;
    printf("source is    :\\n%s\\n",source);//加密前
    ptr_en=my_encrypt(source,PUBLICKEY);//加密
    printf("after encrypt:\\n%s\\n",ptr_en);//加密后
    ptr_de=my_decrypt(ptr_en,OPENSSLKEY);//解密
    printf("after decrypt:\\n%s\\n",ptr_de);//解密后
    if(ptr_en!=NULL){
        free(ptr_en);
    }
    if(ptr_de!=NULL){
        free(ptr_de);
    }
    return 0;
}
//加密
char *my_encrypt(char *str,char *path_key){
    char *p_en;
    RSA *p_rsa;
    FILE *file;
    int flen,rsa_len;
    if((file=fopen(path_key,"r"))==NULL){
        perror("open key file error");
        return NULL;
    }
    if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){//读公钥
        ERR_print_errors_fp(stdout);
        return NULL;
    }
    flen=strlen(str);
    rsa_len=RSA_size(p_rsa);
    p_en=(unsigned char *)malloc(rsa_len+1);
    memset(p_en,0,rsa_len+1);
    if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
        return NULL;
    }//这里进行加密
    RSA_free(p_rsa);
    fclose(file);
    return p_en;
}
//解密
char *my_decrypt(char *str,char *path_key){
    char *p_de;
    RSA *p_rsa;
    FILE *file;
    int rsa_len;
    if((file=fopen(path_key,"r"))==NULL){
        perror("open key file error");
        return NULL;
    }
    if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){//读私钥
        ERR_print_errors_fp(stdout);
        return NULL;
    }
    rsa_len=RSA_size(p_rsa);
    p_de=(unsigned char *)malloc(rsa_len+1);
    memset(p_de,0,rsa_len+1);
    if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
        return NULL;
    }//这里进行解密
    RSA_free(p_rsa);
    fclose(file);
    return p_de;
}

如下图为执行结果:

以上是关于公钥私钥与证书 —— 相关内容分析与实践的主要内容,如果未能解决你的问题,请参考以下文章

https原理及实践

SSL中,公钥,私钥,证书的后缀名都是些什么

将私钥与 .net 中的 X509Certificate2 类关联

公钥、私钥、签名、证书之间傻傻分不清

从jks证书中提取公钥和私钥(jks证书转pem证书)

PFX证书拆分私钥与证书