openssl密码库_OpenSSL入门:密码学基础

openssl密码库

本文是使用OpenSSL的两种加密基础知识的第一篇, OpenSSL是Linux和其他系统上流行的生产级库和工具包。 (要安装OpenSSL的最新版本,请参见此处 。)命令行中提供了OpenSSL实用程序,程序可以从OpenSSL库中调用函数。 本文的示例程序使用C语言,即OpenSSL库的源语言。

本系列的两篇文章共同介绍了加密哈希,数字签名,加密和解密以及数字证书。 您可以从我的网站的ZIP文件中找到代码和命令行示例。

让我们首先回顾一下OpenSSL名称中的SSL。

快速的历史

安全套接字层(SSL)Netscape在1995年发布的一种加密协议。该协议层可以位于HTTP之上,从而为HTTPS中的安全提供S。 SSL协议提供了各种安全服务,其中包括两项在HTTPS中居中的服务:

  • 对等身份验证(又称相互挑战):连接的每一边都对另一边的身份进行身份验证。 如果爱丽丝和鲍勃要通过SSL交换消息,则彼此首先验证彼此的身份。
  • 机密性:发件人在通过通道发送消息之前先对其进行加密。 接收器然后解密每个接收到的消息。 此过程可保护网络对话。 即使窃听者Eve截获了从Alice到Bob的加密消息( 中间人攻击),Eve仍发现在计算上无法解密此消息。

反过来,这两个关键SSL服务与其他引起较少关注的服务相关。 例如,SSL支持消息完整性,从而确保接收到的消息与发送的消息相同。 此功能是通过散列函数实现的,散列函数也随OpenSSL工具包一起提供。

SSL已版本化(例如SSLv2和SSLv3),并且在1999年,传输层安全性(TLS)成为基于SSLv3的类似协议。 TLSv1和SSLv3相似,但不足以相互配合。 但是,通常将SSL / TLS称为同一协议。 例如,即使正在使用TLS(而不是SSL),OpenSSL函数也经常在名称中包含SSL。 此外,调用OpenSSL命令行实用程序以术语openssl开头。

除了手册页之外,OpenSSL的文档也参差不齐,考虑到OpenSSL工具包的规模,它们变得笨拙。 命令行和代码示例是将主要主题集中在一起的一种方法。 让我们从一个熟悉的示例开始-使用HTTPS访问网站-并使用该示例来挑选感兴趣的加密片段。

HTTPS客户端

此处显示的客户端程序通过HTTPS连接到Google:


   
   
/* compilation: gcc -o client client.c -lssl -lcrypto */

#include <stdio.h>

#include <stdlib.h>

#include <openssl/bio.h> /* BasicInput/Output streams */

#include <openssl/err.h> /* errors */

#include <openssl/ssl.h> /* core library */

#define BuffSize 1024

void report_and_exit ( const char * msg ) {
  perror ( msg ) ;
  ERR_print_errors_fp ( stderr ) ;
  exit ( - 1 ) ;
}

void init_ssl ( ) {
  SSL_load_error_strings ( ) ;
  SSL_library_init ( ) ;
}

void cleanup ( SSL_CTX * ctx , BIO * bio ) {
  SSL_CTX_free ( ctx ) ;
  BIO_free_all ( bio ) ;
}

void secure_connect ( const char * hostname ) {
  char name [ BuffSize ] ;
  char request [ BuffSize ] ;
  char response [ BuffSize ] ;

  const SSL_METHOD * method = TLSv1_2_client_method ( ) ;
  if ( NULL == method ) report_and_exit ( "TLSv1_2_client_method..." ) ;

  SSL_CTX * ctx = SSL_CTX_new ( method ) ;
  if ( NULL == ctx ) report_and_exit ( "SSL_CTX_new..." ) ;

  BIO * bio = BIO_new_ssl_connect ( ctx ) ;
  if ( NULL == bio ) report_and_exit ( "BIO_new_ssl_connect..." ) ;

  SSL * ssl = NULL ;

  /* link bio channel, SSL session, and server endpoint */

  sprintf ( name , "%s:%s" , hostname , "https" ) ;
  BIO_get_ssl ( bio , & ssl ) ; /* session */
  SSL_set_mode ( ssl , SSL_MODE_AUTO_RETRY ) ; /* robustness */
  BIO_set_conn_hostname ( bio , name ) ; /* prepare to connect */

  /* try to connect */
  if ( BIO_do_connect ( bio ) <= 0 ) {
    cleanup ( ctx , bio ) ;
    report_and_exit ( "BIO_do_connect..." ) ;
  }

  /* verify truststore, check cert */
  if ( ! SSL_CTX_load_verify_locations ( ctx ,
                                      "/etc/ssl/certs/ca-certificates.crt" , /* truststore */
                                      "/etc/ssl/certs/" ) ) /* more truststore */
    report_and_exit ( "SSL_CTX_load_verify_locations..." ) ;

  long verify_flag = SSL_get_verify_result ( ssl ) ;
  if ( verify_flag != X509_V_OK )
    fprintf ( stderr ,
            "##### Certificate verification error (%i) but continuing... \n " ,
            ( int ) verify_flag ) ;

  /* now fetch the homepage as sample data */
  sprintf ( request ,
          "GET / HTTP/1.1 \x0D \x0A Host: %s \x0D \x0A \x43 onnection: Close \x0D \x0A \x0D \x0A " ,
          hostname ) ;
  BIO_puts ( bio , request ) ;

  /* read HTTP response from server and print to stdout */
  while ( 1 ) {
    memset ( response , ' \0 ' , sizeof ( response ) ) ;
    int n = BIO_read ( bio , response , BuffSize ) ;
    if ( n <= 0 ) break ; /* 0 is end-of-stream, < 0 is an error */
  puts ( response ) ;
  }

  cleanup ( ctx , bio ) ;
}

int main ( ) {
  init_ssl ( ) ;

  const char * hostname = "www.google.com:443" ;
  fprintf ( stderr , "Trying an HTTPS connection to %s... \n " , hostname ) ;
  secure_connect ( hostname ) ;

return 0 ;
}

可以从命令行编译和执行此程序(请注意-lssl-lcrypto中的小写L):

GCC -o 客户端 .C - lssl - lcrypto

该程序尝试打开与网站www.google.com的安全连接。 作为与Google Web服务器的TLS握手的一部分, 客户端程序会接收一个或多个数字证书,该程序会尝试对其进行验证(但在我的系统上失败)。 尽管如此, 客户端程序仍继续通过安全通道获取Google主页。 该程序取决于前面提到的安全工件,尽管在代码中仅数字证书突出。 其他文物仍在后台,稍后将对其进行详细说明。

通常,打开HTTP(非安全)通道的C或C ++客户端程序将使用诸如网络套接字的 文件描述符之类的构造,该套接字是两个进程之间连接的端点(例如,客户端程序和Google Web服务器)。 反过来,文件描述符是一个非负整数值,它在程序内标识该程序打开的任何类似文件的构造。 这样的程序还将使用一种结构来指定有关Web服务器地址的详细信息。

这些相对较低级别的构造都不会在客户端程序中发生,因为OpenSSL库会将套接字基础结构和地址规范包装在高级安全构造中。 结果是一个简单的API。 首先看一下示例客户端程序中的安全性详细信息。

  • 该程序首先加载相关的OpenSSL库,然后我的函数init_ssl对OpenSSL进行两次调用:

    SSL_library_init(); SSL_load_error_strings();

  • 下一个初始化步骤尝试获取安全上下文 ,这是建立和维护通往Web服务器的安全通道所需的信息框架。 在示例中使用TLS 1.2 ,如对OpenSSL库函数的此调用所示:

    const SSL_METHOD *方法= TLSv1_2_client_method(); / * TLS 1.2 * /

    如果调用成功,则方法指针将传递到创建SSL_CTX类型上下文的库函数:

    SSL_CTX * ctx = SSL_CTX_new (方法);

    客户端程序检查每个关键库调用中的错误,然后,如果其中一个调用失败,则程序终止。

  • 现在还可以使用另外两个OpenSSL工件: SSL类型的安全会话,该会话从头到尾管理安全连接; BIO (基本输入/输出)类型的安全流,用于与Web服务器通信。 BIO流是通过以下调用生成的:

    BIO * bio = BIO_new_ssl_connect( ctx );

    请注意,最重要的上下文是参数。 BIO类型是C中FILE类型的OpenSSL包装器。此包装器可保护客户端程序和Google的Web服务器之间的输入和输出流。

  • 有了SSL_CTXBIO ,该程序便在SSL会话中将它们链接在一起。 三个库调用可以完成工作:

    BIO_get_ssl(bio,&ssl); / *获得TLS会话* /

    SSL_set_mode(ssl,SSL_MODE_AUTO_RETRY); / *健壮性* /

    BIO_set_conn_hostname(bio,name); / *准备连接到Google * /

    安全连接本身是通过以下调用建立的:

    BIO_do_connect(bio);

    如果最后一次调用不成功,则客户端程序终止;否则, 客户端程序终止。 否则,连接已准备就绪,可以支持客户端程序与Google Web服务器之间的机密对话。

在与Web服务器握手期间, 客户端程序会接收一个或多个数字证书,以认证服务器的身份。 但是, 客户端程序不会发送自己的证书,这意味着身份验证是单向的。 (通常将Web服务器配置为希望获得客户端证书。)尽管Web服务器证书的验证失败,但是客户端程序仍继续通过安全通道将Google主页获取到Web服务器,从而继续执行该程序。

为什么验证Google证书的尝试失败? 典型的OpenSSL安装目录为/ etc / ssl / certs ,其中包含ca-certificates.crt文件。 该目录和文件一起包含OpenSSL开箱即用信任的数字证书,因此构成了信任库 。 可以根据需要更新信任库,尤其是可以包括新信任的证书,并删除不再受信任的证书。

客户端程序从Google Web服务器接收了三个证书,但是我的计算机上的OpenSSL信任库不包含完全匹配项。 如目前所写, 客户端程序不会通过例如验证Google证书上的数字签名(证明该证书的签名)来解决此问题。 如果该签名是受信任的,则包含该签名的证书也应受信任。 尽管如此,客户端程序仍继续进行获取,然后打印Google的主页。 下一节将更详细地介绍。

客户端程序中隐藏的安全性部分

让我们从客户端示例中的可见安全工件(数字证书)开始,并考虑其他安全工件如何与之相关。 数字证书的主要布局标准是X509,生产级证书由诸如Verisign的证书颁发机构(CA)颁发。

数字证书包含各种信息(例如,激活和到期日期以及所有者的域名),包括发行者的身份和数字签名 ,该信息是加密的密码哈希值。 证书还具有未加密的哈希值,用作其识别指纹

将任意数量的位映射到固定长度的摘要会产生哈希值。 这些位所代表的内容(会计报告,小说或数字电影)无关紧要。 例如,消息摘要版本5(MD5)哈希算法将任意长度的输入位映射到128位哈希值,而SHA1(安全哈希算法版本1)算法将输入位映射到160位值。 不同的输入位会导致不同的(实际上是统计学上唯一的)哈希值。 下一篇文章将进一步详细介绍,并将重点放在使散列函数成为密码的方面

数字证书的类型不同(例如, 证书, 中间证书和最终实体证书),并形成反映这些类型的层次结构。 顾名思义, 证书位于层次结构的顶部,其下的证书继承了根证书所具有的信任。 OpenSSL库和大多数现代编程语言都具有X509类型以及处理此类证书的功能。 来自Google的证书具有X509格式,并且客户端程序检查此证书是否为X509_V_OK

X509证书基于公共密钥基础结构(PKI),其中包括用于生成密钥对的算法-RSA是占主导地位的算法-公钥及其配对的私钥。 公共密钥是一种身份: Amazon的公共密钥可以标识它,而我的公共密钥可以标识我。 私钥应由其所有者保守秘密。

一对钥匙具有标准用途。 可以使用公共密钥对消息进行加密,然后可以使用同一对中的私有密钥对消息进行解密。 私钥也可以用于对文档或其他电子产品(例如程序或电子邮件)进行签名,然后可以使用该对中的公钥来验证签名。 以下两个示例填充了一些细节。

在第一个示例中,爱丽丝将她的公钥分发给世界,包括鲍勃。 然后,Bob使用Alice的公钥加密消息,然后将加密的消息发送给Alice。 用爱丽丝的公钥加密的消息用她的私钥解密,而她的私钥(假设)只有她自己,例如:


   
   
             +------------------+ encrypted msg  +-------------------+
Bob's msg--->|Alice's public key|--------------->|Alice's private key|---> Bob's msg
             +------------------+                +-------------------+

原则上可以在没有Alice私钥的情况下解密消息,但在实际情况下,如果使用像RSA这样的加密密钥对系统,则无法实现。

现在,对于第二个示例,考虑对文档进行签名以证明其真实性。 签名算法使用一对中的私钥来处理要签名的文档的加密哈希:


   
   
                    +-------------------+
Hash of document--->|Alice's private key|--->Alice's digital signature of the document
                    +-------------------+

假设Alice以数字方式签署了发送给Bob的合同。 然后,Bob可以使用密钥对中的Alice的公钥来验证签名:


   
   
                                             +------------------+
Alice's digital signature of the document--->|Alice's public key|--->verified or not
                                             +------------------+

没有Alice的私钥就无法伪造Alice的签名:因此,将其私钥保密是符合Alice的利益的。

客户端程序中,除了数字证书以外,这些安全性都没有明确规定。 下一篇文章使用使用OpenSSL实用程序和库函数的示例填充详细信息。

命令行中的OpenSSL

同时,让我们看一下OpenSSL命令行实用程序:尤其是在TLS握手期间检查来自Web服务器的证书的实用程序。 调用OpenSSL实用程序从openssl命令开始,然后添加参数和标志的组合以指定所需的操作。

考虑以下命令:

openssl list-cipher-algorithms

输出是组成密码套件的相关算法的列表。 这是列表的开始,带有注释以澄清首字母缩写词:


   
   
AES-128-CBC ## Advanced Encryption Standard, Cipher Block Chaining
AES-128-CBC-HMAC-SHA1 ## Hash-based Message Authentication Code with SHA1 hashes
AES-128-CBC-HMAC-SHA256 ## ditto, but SHA256 rather than SHA1
...

使用参数s_client的下一个命令将打开到www.google.com的安全连接,并在屏幕上显示有关该连接的所有信息:

OpenSSL的的s_client.First -connect www.google.com:443 - showcerts

端口号443是Web服务器用于接收HTTPS而不是HTTP连接的标准端口号。 (对于HTTP,标准端口为80。)网络地址www.google.com:443也在客户端程序的代码中出现。 如果尝试的连接成功,则将显示来自Google的三个数字证书以及有关安全会话,正在使用的密码套件以及相关项目的信息。 例如,这是从头开始的一部分输出,它宣布证书链即将到来。 证书的编码为base64:


   
   
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=www.google.com
 i:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
-----BEGIN CERTIFICATE-----
MIIEijCCA3KgAwIBAgIQdCea9tmy/T6rK/dDD1isujANBgkqhkiG9w0BAQsFADBU
MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMSUw
...

诸如Google之类的主要网站通常会发送多个证书进行身份验证。

输出以有关TLS会话的摘要信息结尾,包括密码套件的详细信息:


   
   
SSL-Session:
    Protocol : TLSv1.2
    Cipher : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: A2BBF0E4991E6BBBC318774EEE37CFCB23095CC7640FFC752448D07C7F438573
...

客户端程序中使用了协议TLS 1.2 ,并且Session-ID唯一标识了openssl实用程序和Google Web服务器之间的连接。 密码项可以按以下方式进行解析:

  • ECDHE (椭圆曲线Diffie Hellman瞬时)是一种用于管理TLS握手的有效算法。 特别地,ECDHE通过确保连接的双方(例如,客户端程序和Google Web服务器)使用相同的加密/解密密钥(称为会话密钥 )来解决密钥分发问题 。 后续文章深入探讨了细节。

  • RSA (Rivest Shamir Adleman)是主要的公共密钥密码系统,以1970年代末首次描述该系统的三位学者命名。 正在使用的密钥对是使用RSA算法生成的。

  • AES128 (高级加密标准)是一种块密码 ,用于加密和解密位块。 (另一种选择是流密码 ,一次加密和解密一位。)密码是对称的 ,因为使用相同的密钥进行加密和解密,这首先引起了密钥分配问题。 AES支持128(在此处使用),192和256位的密钥大小:密钥越大,保护越好。

    通常,对称密码系统(例如AES)的密钥大小小于非对称(基于密钥对)系统(例如RSA)的密钥大小。 例如,1024位RSA密钥相对较小,而256位密钥当前是AES最大的密钥。

  • GCM (Galois计数器模式)在安全对话期间处理密码(在本例中为AES128)的重复应用。 AES128块的大小仅为128位,安全对话很可能包含从一侧到另一侧的多个AES128块。 GCM非常有效,通常与AES128搭配使用。

  • SHA256 (256位安全哈希算法)是正在使用的加密哈希算法。 生成的哈希值大小为256位,尽管使用SHA甚至可以达到更大的值。

密码套件正在不断发展。 例如,不久前,谷歌使用RC4流密码(Ron的Cipher版本4,来自RSA的Ron Rivest)。 RC4现在具有已知的漏洞,这至少部分是由Google转换为AES128引起的。

结语

通过安全的C Web客户端和各种命令行示例对OpenSSL的首次了解,使少数需要进一步澄清的主题脱颖而出。 下一篇文章将详细介绍 ,从加密散列开始,最后以关于数字证书如何解决密钥分发挑战的更完整的讨论结尾。

翻译自: https://opensource.com/article/19/6/cryptography-basics-openssl-part-1

openssl密码库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值