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
|