08.公钥算法(数字签名和RSA)

时间:2021-04-08 20:27:32
Diffie-Hellman (DH):使用在密钥协议
DSA (Digital Signature Algorithm),:使用在数字签名
RSA ( Rivest, Shamir, and Adleman):使用在密钥协商
 
8.2 Diffie-Hellman
Diffie-Hellman算法是有史以来发明的第一个公钥算法。 Whitfield Diffie和Martin Hellman于1976年推出,它是一个简单的算法,允许双方就使用不安全通道的密钥达成一致。 换句话说,它允许创建一个共享的秘密。 这个过程有时被称为密钥交换,但是在Diffie-Hellman中,更准确地称为密钥交换。
 
Diffie-Hellman的主要用途是共享秘密协商。 算法本身可以提供认证,但OpenSSL不包含任何使用这些特性的高级接口,所以如果需要的话,它们必须由应用程序来实现。 出于这个原因,大多数使用这种算法的OpenSSL应用程序也将使用另一个进行身份验证。 就我们的目的而言,我们将主要从密钥协议的角度来讨论Diffie-Hellman。 有兴趣的读者可以参考RFC 2631了解更多关于使用它进行认证的信息。
 
8.2.1 The Basics
在openssl/dh.h
typedef struct dh_st
{
BIGNUM *p; 共享公共值
BIGNUM *g;共享公共值
BIGNUM *pub_key;
BIGNUM *priv_key;
} DH;
 
8.2.2 Generating and Exchanging Parameters
 
函数
 
DH *DH_generate_parameters(int prime_len, int generator,
void (*callback)(int, int, void *), void
*cb_arg);
功能
创建DH对象
参数
 
 
prime_len
要生成的素数的大小,以位表示。
 
generator
用于g的值。
 
callback
指向在素数生成过程中将被调用的函数的指针,用于报告素数生成的状态。
 
cb_arg
指向特定于应用程序的数据的指针。
 
 
函数
 
int DH_check(DH *dh, int *codes);
功能
DH检测对象
参数
 
dh
DH对象
codes
 
DH_CHECK_P_NOT_PRIME
 
DH_CHECK_P_NOT_SAFE_PRIME
 
DH_NOT_SUITABLE_GENERATOR
 
DH_UNABLE_TO_CHECK_GENERATOR
 
 
8.2.3 Computing Shared Secrets
 
函数
 
int DH_compute_key(unsigned char *secret,
BIGNUM *pub_key, DH *dh);
 
功能
单独使用生成函数可能是危险的。
虽然生成函数确实对生成的素数进行了有效性检查,
但它可能会生成不适合与算法一起使用的素数。
参数
 
 
secret
 
一个将被用来保存共享密钥的缓冲区
 
pub_key
一对公钥
 
 
dh
包含参数和调用者的私钥的DH对象
 
 
8.3 Digital Signature Algorithm (DSA)
DSA算法由美国国家标准与测试研究院(NIST)和国家安全局(NSA)开发。 这是1991年首次提出的,引起了很大的争议。 最后,在1994年,它成为一个标准。 顾名思义,DSA算法对计算数字签名非常有用,但这是唯一可以使用的方法。 它不能提供没有扩展的密钥协议或加密。
 
8.3.1 The Basics
openssl/dsa.h
typedef struct dsa_st
{
BIGNUM *p;
BIGNUM *q;
BIGNUM *g;
BIGNUM *pub_key;
BIGNUM *priv_key;
} DSA;
 
 
8.3.2 Generating Parameters and Keys
 
函数
 
DSA * DSA_generate_parameters(int bits, unsigned char *seed, int
seed_len,
int *counter_ret, unsigned long *h_ret,
void (*callback)(int, int, void *),
功能
生成DSA参数的接口类似于生成DiffieHellman参数的接口。
参数
 
 
bits
要生成的素数的大小,以位表示。
 
seed
一个可选的包含数据的缓冲区,为函数提供一个开始查找的起点
为素数
seed_len
种子缓冲区中包含的字节数。
 
counter_ret
一个可选参数,将接收函数经过的迭代次数,以找到满足p和q要求的素数。
 
h_ret
一个可选参数,将接收函数经过的迭代次数,以找到h的值,这是用来计算g的随机数。
 
callback
指向在素数生成过程中将被调用的函数的指针,用于报告素数生成的状态。
 
cb_arg
指向特定于应用程序的数据的指针。
 
 
8.3.3 Signing and Verifying
数字签名必须通过将要签名的数据的消息摘要进行计算。这不是巧合。 实际上,DSA算法不能用于计算大于其q参数的数据的签名。
 
函数
 
int DSA_sign_setup(DSA *dsa, BN_CTX *ctx,
BIGNUM **kinvp, BIGNUM **rp);
 
功能
数字签名必须通过将要签名的数据的消息摘要进行计算。
参数
 
dsa
包含将用于签名的参数和私钥的DSA对象。
 
ctx
将在预计算中使用的可选BIGNUM上下文。
 
kinvp
接收一个动态分配的BIGNUM,它将保存预先计算的kinv值。
 
rp
接收一个动态分配的BIGNUM,它将保存预先计算的r值。
 
 
函数
 
int DSA_sign(int type, const unsigned char *dgst, int len,
unsigned char *sigret, unsigned int *siglen, DSA *dsa);
功能
提供函数DSA_sign用于使用DSA算法实际计算数字签名。
参数
 
 
type
忽略DSA签名。
dgst
将被签名的数据的缓冲区,应始终是SHA1哈希。
 
 
len
 
数据缓冲区中用于签名的字节数。
sigret
一个将接收签名的缓冲区。
 
siglen
接收填充签名的sigret缓冲区的字节数。
 
dsa
用于签署数据缓冲区内容的DSA对象。
 
 
函数
 
int DSA_verify(int type, const unsigned char *dgst, int len,
unsigned char *sigbuf, int siglen, DSA *dsa);
功能
函数DSA_verify用于验证签名,与用于创建签名的函数类似。
参数
 
 
type
忽略DSA验证。
dgst
将被签名的数据的缓冲区,应始终是SHA1哈希。
 
 
len
 
数据缓冲区中用于签名的字节数。
sigbuf
将被验证的签名
 
siglen
接收填充签名的缓冲区的sigbuf
字节数。
 
dsa
用于验证签名的DSA对象。
 
 
8.4 RSA
RSA发明于1977年,是第一个能够对数据进行数字签名和加密的公钥算法。 尽管它已经获得专利,但事实上已经成为公钥算法。 几乎所有在其产品中使用公钥密码术的公司都获得了该技术的许可。 该专利仅在美国获得专利,专利于2000年9月20日到期。
 
8.4.1 The Basics
typedef struct rsa_st
{
BIGNUM *p;
BIGNUM *q;
BIGNUM *n;
BIGNUM *e;(e使用(p-1)(q-1))
BIGNUM *d; (使用e,p和q计算)
} RSA
 
8.4.2 Generating Keys
函数
 
RSA *RSA_generate_key(int num, unsigned long e,
void (*callback)(int, int, void *), void
*cb_arg);
 
功能
RSA算法不需要参数来生成密钥,这使得密钥生成变得非常简单。
参数
 
num
指定公共模数中的位数。
e
用于公共指数的值。
callback
指向在素数生成过程中将被调用的函数的指针,用于报告素数生成的状态。
 
cb_arg
 
指向特定于应用程序的数据的指针。
 
 
8.4.3 Data Encryption, Key Agreement, and Key Transport
现在我们已经探索了关键的设置,我们可以看看使用这些关键字的不同方法。 RSA算法允许保密,因为它可以加密数据。 用公钥加密的数据只能由拥有相应私钥的实体解密。
 
函数
 
int RSA_public_encrypt(int flen, unsigned char *from, unsigned char
*to,RSA *rsa, int padding);
 
 
功能
RSA公钥加密数据
参数
 
flen
指定要加密的缓冲区中的字节数。
from
包含要加密的数据的缓冲区。
to
将用于保存加密数据的缓冲区。
 
rsa
 
包含用于执行加密的公钥的RSA对象。
 
padding
 
指定在需要填充时应使用OpenSSL支持哪些内置填充类型。
 
 
函数
int RSA_private_decrypt(int flen, unsigned char *from, unsigned char
*to,RSA *rsa, int padding);
 
功能
RSA私钥解密数据
参数
 
flen
指定要加密的缓冲区中的字节数。
from
包含要加密的数据的缓冲区。
to
将用于保存加密数据的缓冲区。
 
rsa
 
包含用于执行加密的公钥的RSA对象。
 
padding
 
指定在需要填充时应使用OpenSSL支持哪些内置填充类型。
 
padding参数
意义
 
RSA_PKCS1_PADDING
 
如果使用这种填充类型,则要加密的数据长度必须小于
RSA_size(RSA)-11。
 
RSA_PKCS1_OAEP_PADDING
 
 
If this type of padding is used, the length of the data to be encrypted must be smaller than
RSA_size(rsa)-41.
 
 
RSA_SSLV23_PADDING
 
 
This type of padding is an SSL-specific modification to the RSA_PKCS1_PADDING type.
 
 
RSA_NO_PADDING
 
 
This disables automatic padding by the encryption function, and assumes that the caller
will perform the padding.
 
 
8.4.4 Signing and Verifying
函数
 
int RSA_sign(int type, unsigned char *m, unsigned int m_len,
unsigned char *sigret, unsigned int *siglen, RSA *rsa)
 
功能
RSA签名
参数
 
 
type
 
消息摘要算法,用于获取要签名的数据的加密哈希。
 
m
 
包含要签名的数据的缓冲区。
 
m_len
 
将在签名中考虑的数据缓冲区中的字节数。
 
sigret
 
签名数据
 
siglen
 
签名长度
 
rsa
 
包含用于签署数据的私钥的RSA对象
 
 
函数
 
int RSA_verify(int type, unsigned char *m, unsigned int m_len,
unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
 
功能
RSA验签
参数
 
 
type
 
消息摘要算法,用于获取要签名的数据的加密哈希。
 
m
 
包含要签名的数据的缓冲区。
 
m_len
 
将在签名中考虑的数据缓冲区中的字节数。
 
sigbuf
 
签名数据
 
siglen
 
签名长度
 
rsa
 
包含用于签署数据的私钥的RSA对象
 
 
8.4.5 Practical Applications
 
One-way authenticating key transport
 
客户端选择一个随机的会话密钥,用服务器的公钥加密它
将加密的数据发送到服务器。
 
Two-way authenticating key transport
 
本质上,客户端采取与上述协议相同的步骤,但在发送之前
消息到服务器,它签署消息。
 
Two-way authenticating key agreement
 
客户端和服务器都选择随机数据作为密钥资料,用对方的公钥加密,用自己的私钥签名,然后发送
数据给对方。
 
8.5 The EVP Public Key Interface
 
EVP_PKEY_assign_DSA
 
EVP_PKEY_assign_RSA
 
 
 
 
 
EVP_PKEY_set1_DSA
 
EVP_PKEY_set1_RSA
 
 
 
 
The functions' signatures are
identical to EVP_PKEY_assign_DSA and EVP_PKEY_assign_RSA.
 
 
EVP_PKEY_get1_DSA
 
EVP_PKEY_get1_RSA
 
 
 
 
 
DSA_free
 
RSA_free
 
 
 
 
 
 
8.5.1 Signing and Verifying
EVP接口提供了一种使用DSA或DSA创建和验证数字签名的方法RSA密钥。
函数
 
int EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type);
 
功能
初始化一个EVP_MD_CTX对象
参数
 
 
ctx
 
 
EVP_MD_CTX 初始化
 
type
 
用于计算散列的消息摘要,实际上是用符号来代替数据本身。
 
 
 
int EVP_SignUpdate(EVP_MD_CTX *ctx, const void *buf, unsigned int
len);
 
一旦上下文已经被初始化,要被签名的数据必须被输入。
 
int EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *sig, unsigned int
*siglen,EVP_PKEY *pkey);
 
对于初始化的上下文,应根据需要多次调用EVP_VerifyUpdate
为上下文提供所有据称用于创建签名的数据。
 
int EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *buf, unsigned int
len);
 
一旦所有数据已经与EVP_VerifyUpdate一起传递到上下文中,则必须调用EVP_VerifyFinal来执行签名的实际验证。...
 
int EVP_VerifyFinal(EVP_MD_CTX *ctx, unsigned char *sigbuf,
unsigned int siglen, EVP_PKEY *pkey);
ctx
 
一旦所有数据已经与EVP_VerifyUpdate一起传递到上下文中,则必须调用EVP_VerifyFinal来执行签名的实际验证。...
 
 
8.5.2 Encrypting and Decrypting
EVP接口还提供使用RSA密钥封装数据的接口。
函数
 
int EVP_SealInit(EVP_CIPHER_CTX *ctx, EVP_CIPHER *type,
unsigned char **ek, int *ekl, unsigned char *iv,
EVP_PKEY **pubk, int npubk);
 
功能
上下文对象的初始化通过调用EVP_SealInit来实现。
参数
 
ctx
 
type
用于执行实际加密的对称密码。
ek
一组缓冲区。 必须为数组分配与为指定加密的公钥一样多的元素。
ekl
 
一个数组,将接收每个公钥的实际加密密钥长度。
iv
包含要使用的初始化向量的缓冲区。
pubk
用于加密随机生成的会话密钥的公钥数组。
npubk
公钥密钥数组
 
函数
功能
 
EVP_SealUpdate
 
 
 
EVP_SealFinal
 
 
 
EVP_OpenInit
 
函数EVP_OpenInit初始化解密的上下文。
 
EVP_OpenUpdate
 
初始化上下文解密会话密钥并准备解密数据的上下文
使用指定的对称密码。
 
EVP_OpenFinal
 
一旦所有要解密的数据都被送入EVP_OpenUpdate,就应该调用EVP_OpenFinal来完成这个工作。
 
8.6 Encoding and Decoding Objects
8.6.1 Writing and Reading DER-Encoded Objects
int i2d_OBJNAME(OBJTYPE *obj, unsigned char **pp);
OBJTYPE *d2i_OBJNAME(OBJTYPE **obj, unsigned char **pp, long length);
 
 
Table 8-1. Functions for reading and writing DER encodings of public key objects
Type of object
OpenSSL
object type
Function to write the DER
representation
Function to read the DER
representation
Diffie-Hellman
parameters
DH
i2d_DHparams
d2i_DHParams
DSA parameters
DSA
i2d_DSAparams
d2i_DSAparams
DSA public key
DSA
i2d_DSAPublicKey
d2i_DSAPublicKey
DSA private key
DSA
i2d_DSAPrivateKey
d2i_DSAPrivateKey
RSA public key
RSA
i2d_RSAPublicKey
d2i_RSAPublicKey
RSA private key
RSA
i2d_RSAPrivateKey
d2i_RSAPrivateKey
EVP_PKEY
public key
EVP_PKEY
i2d_PublicKey
d2i_PublicKey
EVP_PKEY
private key
EVP_PKEY
i2d_PrivateKey
d2i_PrivateKey
EVP_PKEY
private key
EVP_PKEY
N/A
d2i_AutoPrivateKey
 
操作步骤:
a.i2d_PublicKey 或 d2i_PublicKey
b.d2i_AutoPrivateKey
c.d2i_PrivateKey_bio或d2i_PrivateKey_fp
d.d2i_AUtoPrivateKey_bio或d2i_AutoPrivateKey_fp
 
 
8.6.2 Writing and Reading PEM-Encoded Objects
函数
 
int PEM_write_OBJNAME(FILE *fp, OBJTYPE *obj, const EVP_CIPHER *enc,
unsigned char *kstr, int klen, pem_password_cb
callback,void *cb_arg);
 
功能
编写私钥的函数有点复杂,因为PEM格式允许在编码和写出之前对它们进行加密。
参数
 
 
fp
 
文件描述符
 
obj
 
用于执行实际加密的对称密码。
 
enc
 
用于加密密钥数据的可选密码。
 
kstr
 
包含用于加密的密码或密码的可选缓冲区。
 
klen
 
指定kstr缓冲区中包含的字节数。
 
callback
 
获取用于加密密钥数据的密码或密码的回调函数。
 
cb_arg
 
 
 
函数
 
typedef int (*pem_password_cb)(char *buf, int len, int rwflag, void
*cb_arg);
功能
用于写入BIO对象的函数具有相同的签名,不同之处在于FILE对象被替换为BIO对象。
参数
 
buf
密码或密码将被写入的缓冲区。
 
len
 
密码缓冲区的大小
 
 
rwflag
 
 
指示是使用密码还是密码短语来加密或解密PEM数据。 在编写PEM数据时,这个参数将是非零的。 在阅读PEM数据时,这个参数将是零。
 
cb_arg
 
 
 
 
 
函数
 
OBJTYPE *PEM_read_OBJNAME(FILE *fp, OBJTYPE **obj, pem_password_cb
callback,void *cb_arg);
 
功能
读公钥,私钥和参数的功能都有相同的签名。
参数
 
 
fp
 
 
 
 
obj
 
 
要用读取的数据填充的对象。
 
 
callback
 
 
 
获取密码或密码(如果需要)的回调函数。
 
cb_arg
 
 
 
 
 
Table 8-2. Functions for reading and writing PEM encodings of public key objects
Type of
object
OpenSSL
object
type
Function to write the PEM
representation
Function to read the PEM
representation
Diffie
Hellman
parameters
DH
PEM_write_DHparams
PEM_write_bio_DHparams
PEM_read_DHparams
PEM_read_bio_DHparams
DSA
parameters
DSA
PEM_write_DSAparams
PEM_write_bio_DSAparams
PEM_read_DSAparams
PEM_read_bio_DSAparams
DSA
public key
DSA
PEM_write_DSA_PUBKEY
PEM_write_bio_DSA_PUBKEY
PEM_read_DSA_PUBKEY
PEM_read_bio_DSA_PUBKEY
DSA
private key
DSA
PEM_write_DSAPrivateKey
PEM_write_bio_DSAPrivateKey
PEM_read_DSAPrivateKey
PEM_read_bio_DSAPrivateKey
RSA
public key
RSA
PEM_write_RSA_PUBKEY
PEM_write_bio_RSA_PUBKEY
PEM_read_RSA_PUBKEY
PEM_read_bio_RSA_PUBKEY
RSA
private key
RSA
PEM_write_RSAPrivateKey
PEM_write_bio_RSAPrivateKey
PEM_read_RSAPrivateKey
PEM_read_bio_RSAPrivateKey
EVP_PKEY
public key
EVP_PKEY
PEM_write_PUBKEY
PEM_write_bio_PUBKEY
PEM_read_PUBKEY
PEM_read_bio_PUBKEY
EVP_PKEY
private key
EVP_PKEY
PEM_write_PrivateKey
PEM_write_bio_PrivateKey
PEM_read_PrivateKey
PEM_read_bio_PrivateKey