分类 OpenSSL 下的文章

“OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连线者的身份。这个包广泛被应用在互联网的网页服务器上。 其主要库是以C语言所写成,实现了基本的加密功能,实现了SSL与TLS协议。”

作用:
主要就是解析ASN.1 BER编码的二进制数据,ASN.1本身只定义了表示信息的抽象句法,但是没有限定其编码的方法。各种ASN.1编码规则提供了由ASN.1描述其抽象句法的数据的值的传送语法(具体表达)。标准的ASN.1编码规则有基本编码规则(BER,Basic Encoding Rules)、规范编码规则(CER,Canonical Encoding Rules)、唯一编码规则(DER,Distinguished Encoding Rules)、压缩编码规则(PER,Packed Encoding Rules)和XML编码规则(XER,XML Encoding Rules)。
功能描述:
ASN1_get_object() parses the identifier and length octets of a BER-encoded value. On function entry, ber_in is expected to point to the first identifier octet. If the identifier and length octets turn out to be valid, the function advances ber_in to the first content octet before returning.

If the identifier octets are valid, ASN1_get_object() stores the tag number in ptag and the class of the tag in pclass. The class is either V_ASN1_UNIVERSAL or V_ASN1_APPLICATION or V_ASN1_CONTEXT_SPECIFIC or V_ASN1_PRIVATE.

If the length octets are valid, too, ASN1_get_object() stores the number encoded in the length octets in plength. If the length octet indicates the indefinite form, plength is set to 0.

ASN1_get_object() inspects at most omax bytes. If parsing of the length octets remains incomplete after inspecting that number of bytes, parsing fails with ASN1_R_HEADER_TOO_LONG.

返回值:
Bits set in the return value of ASN1_get_object() have the following meanings:

0x80
An error occurred. One of the ERRORS described below has been set.
0x20 = V_ASN1_CONSTRUCTED
The encoding is constructed rather than primitive, and the identifier and length octets are valid.
0x01
The length octet indicates the indefinite form. This bit can only occur if V_ASN1_CONSTRUCTED is also set.
Consequently, the following combinations can occur:

0x00
A valid primitive encoding.
0x20
A valid constructed encoding, definite form.
0x21
A valid constructed encoding, indefinite form.
0x80
Either a primitive encoding with a valid tag and definite length, but the content octets won't fit into omax, or parsing failed. Use ERR_GET_REASON(3) to distinguish the two cases.
0xa0
A constructed encoding with a valid tag and definite length, but the content octets won't fit into omax.
The bit combinations 0x01, 0x81, and 0xa1 cannot occur as return values.

示例用法:

ASN1_OCTET_STRING* octet_str = X509_EXTENSION_get_data(extension);
const unsigned char* octet_str_data = octet_str->data;
long xlen;
int tag, xclass;
int ret = ASN1_get_object(&octet_str_data, &xlen, &tag, &xclass, octet_str->length);
printf("value: %s\n", octet_str_data);

示例用法二:

static bool asn1_parse_integer(const unsigned char **asn1data_pos, long length, BIGNUM *bn_result) {
    debug_printf("> asn1_parse_integer(%p, %li, %p)\n",
                 (void *) asn1data_pos, length, (void *) bn_result);
    bool result = true;
    long len;
    int ret, tag, xclass;
    ret = ASN1_get_object(asn1data_pos, &len, &tag, &xclass, length);
    if (ret & 0x80) {
        fprintf(stderr, "ASN1_get_object() failed\n");
        result = false;
    }
    if (tag != V_ASN1_INTEGER) {
        fprintf(stderr, "Invalid tag for ASN1: %d (%s)\n", tag, ASN1_tag2str(tag));
        result = false;
    }
    if (result) {
        ASN1_INTEGER *temp = ASN1_INTEGER_new();
        if (!c2i_ASN1_INTEGER(&temp, asn1data_pos, len)) {
            fprintf(stderr, "d2i_ASN1_INTEGER() failed\n");
            result = false;
        }
        ASN1_INTEGER_to_BN(temp, bn_result);
        ASN1_INTEGER_free(temp);
    }

    debug_printf("< asn1_parse_integer(): %u\n", result);
    return result;
}

为了实现在Openssl内部对象结构和标准的DER编码对象之间格式的转换,OpenSSL定义了一组完成该功能的函数,这些函数基本上是以i2d(内部->DER)和d2i(DER->内部)开头的。跟其他各个系列的函数一样。

TYPE *d2i_TYPE(TYPE a, unsigned char pp, long length);
int i2d_TYPE(TYPE a, unsigned char pp);

DESCRIPTION

TYPE is used a placeholder for any of the OpenSSL datatypes, such as X509_CRL.

These functions convert OpenSSL objects to and from their ASN.1/DER encoding. Unlike the C structures which can have pointers to sub-objects within, the DER is a serialized encoding, suitable for sending over the network, writing to a file, and so on.

d2i_TYPE() attempts to decode len bytes at in.If successful a pointer to the TYPE structure is returned and in is incremented to the byte following the parsed data. If a is not NULL then a pointer to the returned structure is also written to *a. If an error occurred then NULL is returned.

On a successful return, if a is not NULL then it is assumed that a contains a valid TYPE structure and an attempt is made to reuse it. This "reuse" capability is present for historical compatibility but its use is strongly discouraged (see BUGS below,and the discussion in the RETURN VALUES section).

i2d_TYPE() encodes the structure pointed to by a into DER format. If out is not NULL, it writes the DER encoded data to the buffer at *out, and increments it to point after the data just written. If the return value is negative an error occurred, otherwise it returns the length of the encoded data.

If out is NULL memory will be allocated for a buffer and the encoded data written to it. In this case out is not incremented and it points to the start of the data just written.

示例代码:
Allocate and encode the DER encoding of an X509 structure:

 int len;
 unsigned char *buf;

 buf = NULL;
 len = i2d_X509(x, &buf);
 if (len < 0)
     /* error */

Attempt to decode a buffer:

X509 *x;
 unsigned char *buf;
 const unsigned char *p;
 int len;

 /* Set up buf and len to point to the input buffer. */
 p = buf;
 x = d2i_X509(NULL, &p, len);
 if (x == NULL)
     /* error */

函数原型:
@brief load a character certification context into system context. If '*cert' is pointed to the
certification, then load certification into it. Or create a new X509 certification object

X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len)
{
    int m = 0;
    int ret;
    X509 *x;

    SSL_ASSERT2(buffer);
    SSL_ASSERT2(len);

    if (cert && *cert) {
        x = *cert;
    } else {
        x = X509_new();
        if (!x) {
            SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_new() return NULL");
            goto failed1;
        }
        m = 1;
    }

    ret = X509_METHOD_CALL(load, x, buffer, len);
    if (ret) {
        SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_METHOD_CALL(load) return %d", ret);
        goto failed2;
    }

    return x;

failed2:
    if (m)
        X509_free(x);
failed1:
    return NULL;
}

ASN1的基本的数据类型一般都有如下函数:new、free、i2d、d2i、i2a、a2i、print、set、get、cmp和dup。其中new、free、i2d、d2i函数通过宏定义实现。new函数用于分配空间,生成ASN1数据结构;free用于释放空间;i2d函数将ASN1数据结构转换为DER编码;d2i将DER编码转换为ASN1数据结构,i2a将内部结构转换为ASCII码,a2i将ASCII码转换为内部数据结构。set函数用于设置ASN1类型的值,get函数用于获取ASN1类型值;print将ASN1类型打印;cmp用于比较ASN1数据结构;dup函数进行数据结构的拷贝。

string ssl_printnum(ASN1_INTEGER *i)
{
    string strret;
    int len;
    char *data;
    BIO *bio = BIO_new(BIO_s_mem());  

    i2a_ASN1_INTEGER(bio, i);
    len = BIO_get_mem_data(bio, &data);
    strret.assign(data, len);
    BIO_free(bio);

    return strret;
}
string ssl_printstr(ASN1_OCTET_STRING *i)
{
    string strret;
    int len;
    char *data;
    BIO *bio = BIO_new(BIO_s_mem());  

    i2a_ASN1_STRING (bio, i, V_ASN1_OCTET_STRING);
    len = BIO_get_mem_data(bio, &data);
    strret.assign(data, len);
    BIO_free(bio);

    return strret;
}

sm2算法的公钥是64字节,私钥是32字节,公钥其实是由坐标点(x,y)组合构成,即每个点都是32字节的大数构成的。但是大多数情况下在使用的时候都是对公钥进行压缩使用,以节省空间。但是根据点压缩方式,我们在使用的时候看到的压缩公钥有两种,一种是02,一种是03。还有04的标识未压缩。
02/03表示一种压缩方式,所以在解压缩的时候根据压缩标志对Y坐标进行不同的反转操作。如何解压缩呢,前面讲到SM2算法采用的曲线公式,因此当知道坐标X就可以根据该公式算出Y坐标,进而得到完整的公钥。

sm2 压缩与还原
简单来说,就是判断最低位是奇数还是偶数,偶数为0(compressed_y_0),奇数为1(compressed_y_1).

ECC/SM2 公钥(点)压缩方法,64字节公钥压缩成33字节公钥。

void compress(const uint8_t *public_key, uint8_t *compressed)
{
    int i;
    for (i = 0; i < 32; ++i)
    {
        compressed[i+1] = public_key[i];
    }
    compressed[0] = 2 + (public_key[32 * 2 - 1] & 0x01);
}

openssl read rsa private key from string

string key = "-----BEGIN RSA PRIVATE KEY-----\n"
"MIIBOgIBAAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WNYtOO1IV5nYFZ/O8W6bsT\n"
"7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQJAPz9p9xP3+NlffkxTXFoK\n"
"dl6WVzs0AmISI48M2iEsw3wS6auo09AUKg70mP0ueU/GlKsVY78CFzSfSLJUtGdY\n"
"AQIhANmovwa/oL5/HwS/IHRNk1r2ZvUnfPTc90zCW9EqZxFhAiEA0IViaiYkqcSw\n"
"IC+uTn+B87OLvGHLX4UKk1CRoMwQn9ECIEJPdJMbwl8G325UxBBqqd/mfYtmkl0P\n"
"DJBoDgz1PB1BAiEAtLdE6bYRBEkAU4S/TizXlTvAGQ2wUiJdXfrvmyoAJmECIBkM\n"
"IODUSpheA8692yYgQ6t/m8EEIVd2I7K8ebZXr2sc\n"
"-----END RSA PRIVATE KEY-----\n";
BIO* bo = BIO_new(BIO_s_mem());
BIO_write(bo, key.c_str(), key.length());
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bo, nullptr, 0, 0 );
RSA* rsa = EVP_PKEY_get1_RSA( pkey );

openssl 默认生成的rsa密码是pkcs1格式的,java的默认格式是pkcs8的。openssl本身对于pkcs8与pkcs1都是支持的。

string key = "-----BEGIN PRIVATE KEY-----\n"
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAsUqDjZOiD96hLGjI\n"
"g+Q4EOrlp0grxY1i047UhXmdgVn87xbpuxPv4yMXLfvpMhABq+/pt2gECZvPI3UE\n"
"BABvMQIDAQABAkA/P2n3E/f42V9+TFNcWgp2XpZXOzQCYhIjjwzaISzDfBLpq6jT\n"
"0BQqDvSY/S55T8aUqxVjvwIXNJ9IslS0Z1gBAiEA2ai/Br+gvn8fBL8gdE2TWvZm\n"
"9Sd89Nz3TMJb0SpnEWECIQDQhWJqJiSpxLAgL65Of4Hzs4u8YctfhQqTUJGgzBCf\n"
"0QIgQk90kxvCXwbfblTEEGqp3+Z9i2aSXQ8MkGgODPU8HUECIQC0t0TpthEESQBT\n"
"hL9OLNeVO8AZDbBSIl1d+u+bKgAmYQIgGQwg4NRKmF4Dzr3bJiBDq3+bwQQhV3Yj\n"
"srx5tlevaxw=\n"
"-----END PRIVATE KEY-----\n";    
BIO* bo = BIO_new(BIO_s_mem());
BIO_write(bo, key.c_str(), key.length());
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bo, nullptr, 0, 0 );
RSA* rsa = EVP_PKEY_get1_RSA( pkey );

openssl 生成rsa 私钥

openssl genrsa -out rsa_private.pem 512

Generating RSA private key, 512 bit long modulus (2 primes)
...........+++++++++++++++++++++++++++
..............+++++++++++++++++++++++++++
e is 65537 (0x010001)

cat rsa_private.pem 

-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WNYtOO1IV5nYFZ/O8W6bsT
7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQJAPz9p9xP3+NlffkxTXFoK
dl6WVzs0AmISI48M2iEsw3wS6auo09AUKg70mP0ueU/GlKsVY78CFzSfSLJUtGdY
AQIhANmovwa/oL5/HwS/IHRNk1r2ZvUnfPTc90zCW9EqZxFhAiEA0IViaiYkqcSw
IC+uTn+B87OLvGHLX4UKk1CRoMwQn9ECIEJPdJMbwl8G325UxBBqqd/mfYtmkl0P
DJBoDgz1PB1BAiEAtLdE6bYRBEkAU4S/TizXlTvAGQ2wUiJdXfrvmyoAJmECIBkM
IODUSpheA8692yYgQ6t/m8EEIVd2I7K8ebZXr2sc
-----END RSA PRIVATE KEY-----

openssl pkcs1转pkcs8

openssl pkcs8 -topk8 -inform pem -in rsa_private.pem -outform pem -nocrypt -out rsa_private_pkcs8.pem

cat rsa_private_pkcs8.pem 

-----BEGIN PRIVATE KEY-----
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAsUqDjZOiD96hLGjI
g+Q4EOrlp0grxY1i047UhXmdgVn87xbpuxPv4yMXLfvpMhABq+/pt2gECZvPI3UE
BABvMQIDAQABAkA/P2n3E/f42V9+TFNcWgp2XpZXOzQCYhIjjwzaISzDfBLpq6jT
0BQqDvSY/S55T8aUqxVjvwIXNJ9IslS0Z1gBAiEA2ai/Br+gvn8fBL8gdE2TWvZm
9Sd89Nz3TMJb0SpnEWECIQDQhWJqJiSpxLAgL65Of4Hzs4u8YctfhQqTUJGgzBCf
0QIgQk90kxvCXwbfblTEEGqp3+Z9i2aSXQ8MkGgODPU8HUECIQC0t0TpthEESQBT
hL9OLNeVO8AZDbBSIl1d+u+bKgAmYQIgGQwg4NRKmF4Dzr3bJiBDq3+bwQQhV3Yj
srx5tlevaxw=
-----END PRIVATE KEY-----

openssl 从rsa私钥中提取公钥

openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem

writing RSA key

cat rsa_public.pem 

-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WN
YtOO1IV5nYFZ/O8W6bsT7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQ==
-----END PUBLIC KEY-----

openssl rsa -in rsa_private_pkcs8.pem -pubout -out rsa_public_pkcs8.pem

writing RSA key

cat rsa_public_pkcs8.pem 

-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WN
YtOO1IV5nYFZ/O8W6bsT7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQ==
-----END PUBLIC KEY-----

openssl rsa 私钥内容构成
私钥(rsa_private.pem)包括:modulus(n), public exponent(e), private exponent(d), prime 1(p), prime 2(q), exponent 1(exp1), exponent 2(exp2) and coefficient(coeff)。公钥(rsa_public.pem)包括:modulus(n) and public exponent(e)。其中n、e、d会被直接用于加密、解密,其它几个用来校验。

openssl read rsa private key from pem file

RSA* getRSA(string pemfile, bool bprikey)
{
    FILE *fp = fopen(pemfile.c_str(), "rb");
    if(!fp)
    {
        return nullptr;
    }

    RSA* rsa = nullptr;
    if(bprikey)
    {
        rsa = PEM_read_RSAPrivateKey(fp, nullptr, nullptr, nullptr);
    }
    else
    {
        rsa = PEM_read_RSA_PUBKEY(fp, nullptr, nullptr, nullptr);
    }
    fclose(fp);
    
    return rsa;
}

openssl PEM与DER文件格式说明
PEM格式就是在DER格式基础上进行BASE64编码,然后添加一些头尾信息或标签组成的,用于说明当前的文件格式,是一种约定俗称,如头信息为” -----BEGIN RSA PRIVATE KEY-----”,尾信息为” -----END RSA PRIVATE KEY-----”,中间的数据部分即是对DER进行base64编码后的结果。
示例:

echo -n 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WNYtOO1IV5nYFZ/O8W6bsT7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQ==' |base64 --decode |hexdump -e '16/1 "%02X " "\n"'

30 5C 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05
00 03 4B 00 30 48 02 41 00 B1 4A 83 8D 93 A2 0F
DE A1 2C 68 C8 83 E4 38 10 EA E5 A7 48 2B C5 8D
62 D3 8E D4 85 79 9D 81 59 FC EF 16 E9 BB 13 EF
E3 23 17 2D FB E9 32 10 01 AB EF E9 B7 68 04 09
9B CF 23 75 04 04 00 6F 31 02 03 01 00 01

echo -n 'MIIBOgIBAAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WNYtOO1IV5nYFZ/O8W6bsT7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQJAPz9p9xP3+NlffkxTXFoKdl6WVzs0AmISI48M2iEsw3wS6auo09AUKg70mP0ueU/GlKsVY78CFzSfSLJUtGdYAQIhANmovwa/oL5/HwS/IHRNk1r2ZvUnfPTc90zCW9EqZxFhAiEA0IViaiYkqcSwIC+uTn+B87OLvGHLX4UKk1CRoMwQn9ECIEJPdJMbwl8G325UxBBqqd/mfYtmkl0PDJBoDgz1PB1BAiEAtLdE6bYRBEkAU4S/TizXlTvAGQ2wUiJdXfrvmyoAJmECIBkMIODUSpheA8692yYgQ6t/m8EEIVd2I7K8ebZXr2sc' |base64 --decode |hexdump -e '16/1 "%02X " "\n"'

30 82 01 3A 02 01 00 02 41 00 B1 4A 83 8D 93 A2
0F DE A1 2C 68 C8 83 E4 38 10 EA E5 A7 48 2B C5
8D 62 D3 8E D4 85 79 9D 81 59 FC EF 16 E9 BB 13
EF E3 23 17 2D FB E9 32 10 01 AB EF E9 B7 68 04
09 9B CF 23 75 04 04 00 6F 31 02 03 01 00 01 02
40 3F 3F 69 F7 13 F7 F8 D9 5F 7E 4C 53 5C 5A 0A
76 5E 96 57 3B 34 02 62 12 23 8F 0C DA 21 2C C3
7C 12 E9 AB A8 D3 D0 14 2A 0E F4 98 FD 2E 79 4F
C6 94 AB 15 63 BF 02 17 34 9F 48 B2 54 B4 67 58
01 02 21 00 D9 A8 BF 06 BF A0 BE 7F 1F 04 BF 20
74 4D 93 5A F6 66 F5 27 7C F4 DC F7 4C C2 5B D1
2A 67 11 61 02 21 00 D0 85 62 6A 26 24 A9 C4 B0
20 2F AE 4E 7F 81 F3 B3 8B BC 61 CB 5F 85 0A 93
50 91 A0 CC 10 9F D1 02 20 42 4F 74 93 1B C2 5F
06 DF 6E 54 C4 10 6A A9 DF E6 7D 8B 66 92 5D 0F
0C 90 68 0E 0C F5 3C 1D 41 02 21 00 B4 B7 44 E9
B6 11 04 49 00 53 84 BF 4E 2C D7 95 3B C0 19 0D
B0 52 22 5D 5D FA EF 9B 2A 00 26 61 02 20 19 0C
20 E0 D4 4A 98 5E 03 CE BD DB 26 20 43 AB 7F 9B
C1 04 21 57 76 23 B2 BC 79 B6 57 AF 6B 1C
此处解码后数据,与下面pem转der的内容一致。
openssl pem 转 der

openssl rsa -in rsa_private.pem -outform der -out rsa_private.der

writing RSA key

hexdump -e '16/1 "%02X " "\n"' rsa_private.der 

30 82 01 3A 02 01 00 02 41 00 B1 4A 83 8D 93 A2
0F DE A1 2C 68 C8 83 E4 38 10 EA E5 A7 48 2B C5
8D 62 D3 8E D4 85 79 9D 81 59 FC EF 16 E9 BB 13
EF E3 23 17 2D FB E9 32 10 01 AB EF E9 B7 68 04
09 9B CF 23 75 04 04 00 6F 31 02 03 01 00 01 02
40 3F 3F 69 F7 13 F7 F8 D9 5F 7E 4C 53 5C 5A 0A
76 5E 96 57 3B 34 02 62 12 23 8F 0C DA 21 2C C3
7C 12 E9 AB A8 D3 D0 14 2A 0E F4 98 FD 2E 79 4F
C6 94 AB 15 63 BF 02 17 34 9F 48 B2 54 B4 67 58
01 02 21 00 D9 A8 BF 06 BF A0 BE 7F 1F 04 BF 20
74 4D 93 5A F6 66 F5 27 7C F4 DC F7 4C C2 5B D1
2A 67 11 61 02 21 00 D0 85 62 6A 26 24 A9 C4 B0
20 2F AE 4E 7F 81 F3 B3 8B BC 61 CB 5F 85 0A 93
50 91 A0 CC 10 9F D1 02 20 42 4F 74 93 1B C2 5F
06 DF 6E 54 C4 10 6A A9 DF E6 7D 8B 66 92 5D 0F
0C 90 68 0E 0C F5 3C 1D 41 02 21 00 B4 B7 44 E9
B6 11 04 49 00 53 84 BF 4E 2C D7 95 3B C0 19 0D
B0 52 22 5D 5D FA EF 9B 2A 00 26 61 02 20 19 0C
20 E0 D4 4A 98 5E 03 CE BD DB 26 20 43 AB 7F 9B
C1 04 21 57 76 23 B2 BC 79 B6 57 AF 6B 1C

openssl asn1parse 使用

openssl asn1parse -i -in rsa_private.pem 

0:d=0 hl=4 l= 314 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=2 l= 65 prim: INTEGER :B14A838D93A20FDEA12C68C883E43810EAE5A7482BC58D62D38ED485799D8159FCEF16E9BB13EFE323172DFBE9321001ABEFE9B76804099BCF23750404006F31
74:d=1 hl=2 l= 3 prim: INTEGER :010001
79:d=1 hl=2 l= 64 prim: INTEGER :3F3F69F713F7F8D95F7E4C535C5A0A765E96573B34026212238F0CDA212CC37C12E9ABA8D3D0142A0EF498FD2E794FC694AB1563BF0217349F48B254B4675801
145:d=1 hl=2 l= 33 prim: INTEGER :D9A8BF06BFA0BE7F1F04BF20744D935AF666F5277CF4DCF74CC25BD12A671161
180:d=1 hl=2 l= 33 prim: INTEGER :D085626A2624A9C4B0202FAE4E7F81F3B38BBC61CB5F850A935091A0CC109FD1
215:d=1 hl=2 l= 32 prim: INTEGER :424F74931BC25F06DF6E54C4106AA9DFE67D8B66925D0F0C90680E0CF53C1D41
249:d=1 hl=2 l= 33 prim: INTEGER :B4B744E9B6110449005384BF4E2CD7953BC0190DB052225D5DFAEF9B2A002661
284:d=1 hl=2 l= 32 prim: INTEGER :190C20E0D44A985E03CEBDDB262043AB7F9BC10421577623B2BC79B657AF6B1C

openssl asn1parse -i -in rsa_public.pem

0:d=0 hl=2 l= 92 cons: SEQUENCE
2:d=1 hl=2 l= 13 cons: SEQUENCE
4:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
15:d=2 hl=2 l= 0 prim: NULL
17:d=1 hl=2 l= 75 prim: BIT STRING

在解析rsa_public.pem文件时没有显示出数据,其中BIT STRING的内容就是公钥PKCS#1格式的公钥数据,若显示数据,需要添加个偏移选项参数-strparse,在这里偏移值是17,添加后的执行结果如下:输出的数据与私钥文件中的n,e相同

openssl asn1parse -i -in rsa_public.pem  -strparse 17

0:d=0 hl=2 l= 72 cons: SEQUENCE
2:d=1 hl=2 l= 65 prim: INTEGER :B14A838D93A20FDEA12C68C883E43810EAE5A7482BC58D62D38ED485799D8159FCEF16E9BB13EFE323172DFBE9321001ABEFE9B76804099BCF23750404006F31
69:d=1 hl=2 l= 3 prim: INTEGER :010001

PKCS 介绍
PKCS即Public Key Cryptography Standards,公钥加密标准,一共有15个标准,编号从1到15。OpenSSL中RSA使用的是PKCS#1。PKCS#1定义了RSA的数理基础、公/私钥格式,以及加/解密、签/验章的流程。

openssl升级后RSA结构的变化
opensslv1.1.1版本之后RSA结构的变化
代码从openssl1.0.2转到openssl 3.0编译时报错:
error: dereferencing pointer to incomplete type ‘RSA’ {aka ‘struct rsa_st’}

不能通过RSA *rsa;
rsa->n去访问内部成员了。
在1.1.1之后,OpenSSL支持getter这样返回每个参数。

const BIGNUM *RSA_get0_n(const RSA *d);
const BIGNUM *RSA_get0_e(const RSA *d);
const BIGNUM *RSA_get0_d(const RSA *d);
const BIGNUM *RSA_get0_p(const RSA *d);
const BIGNUM *RSA_get0_q(const RSA *d);
const BIGNUM *RSA_get0_dmp1(const RSA *r);
const BIGNUM *RSA_get0_dmq1(const RSA *r);
const BIGNUM *RSA_get0_iqmp(const RSA *r);

#The RSA_size() and RSA_security_bits() functions were deprecated in OpenSSL 3.0.

非对称加密算法概述
非对称加密算法也称公开密钥算法,其解决了对称加密算法密钥分配的问题,非对称加密算法基本特点如下:

1、加密密钥和解密密钥不同

2、密钥对中的一个密钥可以公开

3、根据公开密钥很难推算出私人密钥

根据非对称加密算法的特点,可用户数字签名、密钥交换、数据加密。但是由于非对称加密算法较对称加密算法加密速度慢很多,故最常用的用途是数字签名和密钥交换。

目前常用的非对称加密算法有RSA, DH和DSA三种,但并非都可以用于密钥交换和数字签名。而是RSA可用于数字签名和密钥交换,DH算法可用于密钥交换,而DSA算法专门用户数字签名。

openssl 生成RSA代码

RSA* creatRSAkey()
{
    //r存放RSA密钥对
    RSA*r=RSA_new();
    //大数结构体 公钥质数 大质数
    //公钥(E,N)
    BIGNUM *e = BN_new(); //创建好了大数
    BN_set_word(e, RSA_F4); //公钥指数使用默认值 RSA_F4 65537,也可以使用随机值

    //生成私钥指数D和N
    RSA_generate_key_ex(r,
        512, //指定密钥长度为512bit
        e,
        NULL);
    return r;
}

rsa 加密填充模式说明

RSA加密常用的填充方式有下面3种:

1.RSA_PKCS1_PADDING 填充模式,最常用的模式

要求:
输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割, 然后填充

输出:和modulus一样长

根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节

2.RSA_PKCS1_OAEP_PADDING
输入:RSA_size(rsa) – 41

输出:和modulus一样长

3.for RSA_NO_PADDING  不填充

输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充

输出:和modulus一样长

跟DES,AES一样, RSA也是一个块加密算法( block cipher algorithm),总是在一个固定长度的块上进行操作。

但跟AES等不同的是, block length是跟key length有关的。

每次RSA加密的明文的长度是受RSA填充模式限制的,但是RSA每次加密的块长度就是key length。

需要注意:

假如你选择的秘钥长度为1024bit共128个byte:

1.当你在客户端选择RSA_NO_PADDING填充模式时,如果你的明文不够128字节

加密的时候会在你的明文前面,前向的填充零。解密后的明文也会包括前面填充的零,这是服务器需要注意把解密后的字段前向填充的零去掉,才是真正之前加密的明文。

2.当你选择RSA_PKCS1_PADDING填充模式时,如果你的明文不够128字节

加密的时候会在你的明文中随机填充一些数据,所以会导致对同样的明文每次加密后的结果都不一样。

对加密后的密文,服务器使用相同的填充方式都能解密。解密后的明文也就是之前加密的明文。

3.RSA_PKCS1_OAEP_PADDING填充模式没有使用过, 他是PKCS#1推出的新的填充方式,安全性是最高的,

和前面RSA_PKCS1_PADDING的区别就是加密前的编码方式不一样。

RSA 加解密接口
//@flen明文长度@from明文@to密文@rsa密钥@padding填充方式
int RSA_public_encrypt(int flen, const unsigned char *from,

                   unsigned char *to, RSA *rsa, int padding);

int RSA_private_decrypt(int flen, const unsigned char *from,

                    unsigned char *to, RSA *rsa, int padding);

openssl rsa 加解密代码

int RSAEncrypto(unsigned char *data, int datasize, RSA*r, unsigned char *outdata)
{
    //keysize=64字节512bit
    //输入数据大小=64-11=53
    int blocksize = RSA_size(r) - RSA_PKCS1_PADDING_SIZE;
    int outsize = 0;
    for (int i = 0; i < datasize; i += blocksize)
    {
        int ensize = blocksize;
        if (datasize - i < blocksize)
        {
            ensize = datasize - i;
        }
        int outoff = i + RSA_PKCS1_PADDING_SIZE * (i / blocksize);
        int ret = RSA_public_encrypt(ensize, data + i, 
      outdata + outoff, r, RSA_PKCS1_PADDING);
        if (ret < 0)
        {
            ERR_print_errors_fp(stderr);
        }
        outsize = outoff + RSA_size(r);
    }
    return outsize;
}

openssl rsa 解密代码

int RSAdecrypto(unsigned char *data, int datasize, RSA*r, unsigned char*outdata)
{
    int ensize = RSA_size(r);
    int outoff = 0;
    for (int i = 0; i < datasize; i += ensize)
    {
        int len=RSA_private_decrypt(ensize, data+i, 
          outdata+outoff, r, RSA_PKCS1_PADDING);
        outoff += len;
    }
    return outoff;
}

需要注意的是,在openssl3.0中 RSA_size被弃用。

RSA算法相关指令及用法
RSA虽然可以数字签名、密钥交换和数据加密,但是RSA加密数据速度慢,通常不使用RSA加密数据。所以最常用的功能就是数字签名和密钥交换,抛开数字签名和密钥交换的概念,实质上就是使用公钥加密还是使用私钥加密的区别。所以我们只要记住一句话:“公钥加密,私钥签名”。

公钥加密:用途是密钥交换,用户A使用用户B的公钥将少量数据加密发送给B,B用自己的私钥解密数据

私钥签名:用途是数字签名,用户A使用自己的私钥将数据的摘要信息加密一并发送给B,B用A的公钥解密摘要信息并验证

opessl中RSA算法指令主要有三个,其他指令虽有涉及,但此处不再详述。

指令 功能
genrsa 生成并输入一个RSA私钥
rsa 处理RSA密钥的格式转换等问题
rsautl 使用RSA密钥进行加密、解密、签名和验证等运算

openssl genrsa 使用说明

openssl help genrsa

Usage: genrsa [options]
Valid options are:
-help Display this summary
-3 Use 3 for the E value
-F4 Use F4 (0x10001) for the E value
-f4 Use F4 (0x10001) for the E value
-out outfile Output the key to specified file
-rand val Load the file(s) into the random number generator
-writerand outfile Write random data to the specified file
-passout val Output file pass phrase source
-* Encrypt the output with any supported cipher
-engine val Use engine, possibly a hardware device
-primes +int Specify number of primes

openssl 生成带密码加密的rsa 密钥

openssl genrsa -F4 -aes128 -passout pass:123456 -out rsa_private_crypt.pem 512

Generating RSA private key, 512 bit long modulus (2 primes)
..........+++++++++++++++++++++++++++
........+++++++++++++++++++++++++++
e is 65537 (0x010001)

cat rsa_private_crypt.pem 

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,7C229C265FAA4F314755944CCE15A817

5KijAfOakKOLI1IjluKIzH4TRLp5vxJtodKjnp3zhWbZ6tVLJEnDeiwEzcTrAuUH
0ruEc5SfOnZpSIl72Y8RmgtWyyjM0fBpAbmUT+r2gKQHURTw9qy74p0nEhujsxpu
oxrFUGjM/56oYTlCovMsz58fita/BZ3grdxDUFXGO78/s17hQyZV+z+7I/8VyUAJ
DvhkdiwcJ/HKP2ZDMyQzIat4Oa9p7r6Nk1tkfwq8FprCgiWSSA6O5e5kXEJ1y0Gl
oQKtvpaCKxksi5a7ngkrHgxdSOvgA9Kb4L3NSOaDW61svbRsU05IjCBQ73bP/7q4
zXhcI7i1AHcXy76sPkm0wy9kRJ/XzABqCq/4ZyqTqEhk0duebAGeMaVsCFVGn+6q
PGjhCsFP0ZPkmkregY+vRHS9tqC7E3Fopr/ZOoCSLAA3CNPoyz72YG+U5wZo8+TQ
-----END RSA PRIVATE KEY-----

openssl genrsa -F4 -aes128 -passout pass:123456 -out rsa_private_crypt.pem 1024

Generating RSA private key, 1024 bit long modulus (2 primes)
............................+++++
...+++++
e is 65537 (0x010001)

cat rsa_private_crypt.pem 

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,017AC0DBA662ADC280B9F6B3AE7BDAB6

6jpnXH93H44EwWGilh9MFiRwExfJE498j05HriqoL0W/ppoI4KZ4pqJfru5VxqJr
Y/FVMIlsDaphUGHuwRo7xy/V0YlywRNtvgWmEAayD5A527C/z9V8rdaunqJl0tW0
UGfevA1NBtsOuplpWLlH7wVF0Dixa8w5nAqphDc3x/F7NrrmAqqM3jWJt47FxrBV
8UA7a6h+vLb1l9/n34aji4GE8vyY42VQ2hnDMsJmy3SlbP9bqCQTUtQug5sslg3R
0MbNm//oT1Wo4N3BfmdPZkFPH9OPnNxtXZysmPVtvg6WBH9pn7LO0xQh884frM7w
etWqHnHu2G0pNYgTlsE5+6T9jbaNeYv5wJn96nT+kWUesdfcldeNXgolJL+rBrLz
RepAt5pS5R2TIMRriRxyeWgE8veLRsTecDuU7EnmBdQ8BEFFLU1QgD8sJ8i6cTs8
4RA1TpuLgzr1eRh07+QAjuDBi02inQ6bEUW5B8dlkp/48G7Pdcx0poo8PJ1ONK/k
FgLSm70LP7eagK1fxGCty5P6KOfALW69fiV3RlxfG6E3Yxx002weknIQB4qXuVwh
Qo1sLDKQah/xYu0CJ4g4DWt7eBrxjU7nrK1L2hc+i8wGTZkDr+Ww9vF/b1a4R8LZ
quNvGiIf89RQcx54+EyNtEE1LCUzMVUiT3MNrBoe4JlAykE+tnYqqDg2eYUW6DQz
h8QnwpuNyX3fYtmGDV+uB9YBRphIMiQSOxG9NYAe+S/NQyyM3g08AiKUZdyDpAmS
YnYzkc5YgxcRPQDGdiY8TmsKFAqo3IJi+tdlzXnYZS5QiXsTaiwvZznuhCreKBeN
-----END RSA PRIVATE KEY-----

openssl rsa 指令说明

openssl help rsa

Usage: rsa [options]
Valid options are:
-help Display this summary
-inform format Input format, one of DER PEM
-outform format Output format, one of DER PEM PVK
-in val Input file
-out outfile Output file
-pubin Expect a public key in input file
-pubout Output a public key
-passout val Output file pass phrase source
-passin val Input file pass phrase source
-RSAPublicKey_in Input is an RSAPublicKey
-RSAPublicKey_out Output is an RSAPublicKey
-noout Don't print key out
-text Print the key in text
-modulus Print the RSA key modulus
-check Verify key consistency
-* Any supported cipher
-pvk-strong Enable 'Strong' PVK encoding level (default)
-pvk-weak Enable 'Weak' PVK encoding level
-pvk-none Don't enforce PVK encoding
-engine val Use engine, possibly a hardware device

RSA密钥去除口令保护

openssl rsa -in rsa_private_crypt.pem -passin pass:123456 -out rsa_private_plain.pem
cat rsa_private_plain.pem 

-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQCpP8Kf7qB6A8bW75FwQF2FWkbNbMwhaVcNKJUBV2B45FMPcDbo
QzuYb+mWG+/41hL5eHBguI5JHL2weftkZpaeG5rH4S2Uhq3CHGjaDjwaAIDG4gA/
ZL/0fw6Y8852qYgVjqyBCfvXEwDKuHBtJszCqW+ME5qAtgpodMl+QcMjtQIDAQAB
AoGBAJjHJJGoH5ZkyF4HHbs9bu5MgrM27cGPTHRlWLRAQqZ+PPgnrHjXD/nXs/y7
tVBjNfeaH58/mbknx5eBVUvZS0VvtLHgrrUvIZkfNeuSKmpS5L8QyYUm/NDnoNeO
zteMzILicWBiDdf6VawIulLs0pJGji8YKjP9iqzvziJ+RHAVAkEA2S/J8BeyFGcj
W7ITSCAK9GDN+RufG5a0/raSYzAg5W9wDHkL1GgvLbPidRRppAO50Cbbq+eTWl8m
GeamlJheswJBAMd+19r4aEy+9jr8kGnnMbDIktWmuVsomXOSGPUhvKZNak1pL8aK
gxdQEsKQVjxTYOwUcC1uJed/yOyKAoujp/cCQQCQaYdT3t2pVV8cZI8PoUbHcerj
Xetw08frqggybdkh1fRiRsaH6PKd6AOHKBiKV3PhJUVhy6yeJbBW/pf7LEmjAkBy
aMNAWC/wu5+ZCpmDssxjl1PmZZxttCX1Crd3dear9T/er1Fv6qXtq8VmgcKDDEpM
ehgvZbklP7qqNSfqj2vXAkEAgu0UbvbdyiCSSnmshOSL5DT+AagiWUHvA5u/BZOs
waYbf03A1v2U/p9+zzrCcgZv+gyT6Rmctum/voNO4X+QVw==
-----END RSA PRIVATE KEY-----

为RSA密钥增加口令保护

openssl rsa -in rsa_private_plain.pem -passout pass:123456 -out rsa_private_crypt0.pem

writing RSA key

cat rsa_private_crypt0.pem 

-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQCpP8Kf7qB6A8bW75FwQF2FWkbNbMwhaVcNKJUBV2B45FMPcDbo
QzuYb+mWG+/41hL5eHBguI5JHL2weftkZpaeG5rH4S2Uhq3CHGjaDjwaAIDG4gA/
ZL/0fw6Y8852qYgVjqyBCfvXEwDKuHBtJszCqW+ME5qAtgpodMl+QcMjtQIDAQAB
AoGBAJjHJJGoH5ZkyF4HHbs9bu5MgrM27cGPTHRlWLRAQqZ+PPgnrHjXD/nXs/y7
tVBjNfeaH58/mbknx5eBVUvZS0VvtLHgrrUvIZkfNeuSKmpS5L8QyYUm/NDnoNeO
zteMzILicWBiDdf6VawIulLs0pJGji8YKjP9iqzvziJ+RHAVAkEA2S/J8BeyFGcj
W7ITSCAK9GDN+RufG5a0/raSYzAg5W9wDHkL1GgvLbPidRRppAO50Cbbq+eTWl8m
GeamlJheswJBAMd+19r4aEy+9jr8kGnnMbDIktWmuVsomXOSGPUhvKZNak1pL8aK
gxdQEsKQVjxTYOwUcC1uJed/yOyKAoujp/cCQQCQaYdT3t2pVV8cZI8PoUbHcerj
Xetw08frqggybdkh1fRiRsaH6PKd6AOHKBiKV3PhJUVhy6yeJbBW/pf7LEmjAkBy
aMNAWC/wu5+ZCpmDssxjl1PmZZxttCX1Crd3dear9T/er1Fv6qXtq8VmgcKDDEpM
ehgvZbklP7qqNSfqj2vXAkEAgu0UbvbdyiCSSnmshOSL5DT+AagiWUHvA5u/BZOs
waYbf03A1v2U/p9+zzrCcgZv+gyT6Rmctum/voNO4X+QVw==
-----END RSA PRIVATE KEY-----

RSA 修改密钥的保护口令和算法

openssl rsa -in rsa_private_crypt0.pem -passin pass:123456 -aes256 -passout pass:123456 -out rsa_private_crypt1.pem

writing RSA key

cat rsa_private_crypt1.pem 

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,E31EBA8056B42C56F1E6397799DA6DE1

pkaq3xxwHyp6iZ2yZuCmiVxeyYMXB3q6wZrBm0UyYRTHzMHp/xyGnvrFfkphG0LI
8zU1inq+r2XudGQCs5soRbRx2ipyrvPxuxQxkFL1WhmmWs7UfLaENRGJOnorfKR3
s2G4GmQZ3GZ8afR29knb/0sMp4nh2xiF7YmTxhrFxpcrqpDWrLgfYuVv9ssOzTX7
zvSRskrdpOKsODe94rTzZ2h98c7EEvYJvTmof1mtnfG1Ihbf0nX8oXeGUHWAwwff
A6izC/9UeUVTqid7EGCO6Jd5UuN8BmzSiG1Esf/PTve6cEYNwyK9jh4P8B8EYGA5
KvX1sI79ER7ONCWv8s1x+ls8z6EjPthXu8joblzzyiRBHZZRh6ZVIiVstEoj4ITQ
7AMpG/z0lBrj7QXNW/6DBdSkFwCDyhLJrmcCyUrt6vB567H5SEOK/29B9mY+m/N/
zoZ2jLv8qBcH/XxAEAND/ocu/h5M2GE3RYmeAjlG0Ut7R6BdwcDqr1LbGRbvjTWd
ahY9lsaPGRPWtAb0bjuryRbxCgRodJqtEB0zhXMPr+hbrqFIWrlR2yKH57ycnGVB
SbBwTS11KjkCfPbQ2oOuZwNKremb8ZgByMRsVfUHunFz+PRm45jOdZiXjJkEiwT2
BzJi3NvUVU6lGDhVW1fOvdaxyQCM8EXu8k9+zMFLJGtE5T5WLdA4SvalUdi3UIeF
oUomM2po52neQgPOTUaGimt1RxB51Anrhu1T7o0M8HuyX+1UfxohpYSWT1YBZJgs
qfn6xOf9x6ym2TaFozg1+yMmyGDILRAocz6dS8hImJZFGNp5YwgifWmW4eHmTMwX
-----END RSA PRIVATE KEY-----

openssl rsa 查看 rsa密钥相关参数

openssl rsa -in rsa_private_crypt1.pem -passin pass:123456 -text -noout

RSA Private-Key: (1024 bit, 2 primes)
modulus:

00:a9:3f:c2:9f:ee:a0:7a:03:c6:d6:ef:91:70:40:
5d:85:5a:46:cd:6c:cc:21:69:57:0d:28:95:01:57:
60:78:e4:53:0f:70:36:e8:43:3b:98:6f:e9:96:1b:
ef:f8:d6:12:f9:78:70:60:b8:8e:49:1c:bd:b0:79:
fb:64:66:96:9e:1b:9a:c7:e1:2d:94:86:ad:c2:1c:
68:da:0e:3c:1a:00:80:c6:e2:00:3f:64:bf:f4:7f:
0e:98:f3:ce:76:a9:88:15:8e:ac:81:09:fb:d7:13:
00:ca:b8:70:6d:26:cc:c2:a9:6f:8c:13:9a:80:b6:
0a:68:74:c9:7e:41:c3:23:b5

publicExponent: 65537 (0x10001)
privateExponent:

00:98:c7:24:91:a8:1f:96:64:c8:5e:07:1d:bb:3d:
6e:ee:4c:82:b3:36:ed:c1:8f:4c:74:65:58:b4:40:
42:a6:7e:3c:f8:27:ac:78:d7:0f:f9:d7:b3:fc:bb:
b5:50:63:35:f7:9a:1f:9f:3f:99:b9:27:c7:97:81:
55:4b:d9:4b:45:6f:b4:b1:e0:ae:b5:2f:21:99:1f:
35:eb:92:2a:6a:52:e4:bf:10:c9:85:26:fc:d0:e7:
a0:d7:8e:ce:d7:8c:cc:82:e2:71:60:62:0d:d7:fa:
55:ac:08:ba:52:ec:d2:92:46:8e:2f:18:2a:33:fd:
8a:ac:ef:ce:22:7e:44:70:15

prime1:

00:d9:2f:c9:f0:17:b2:14:67:23:5b:b2:13:48:20:
0a:f4:60:cd:f9:1b:9f:1b:96:b4:fe:b6:92:63:30:
20:e5:6f:70:0c:79:0b:d4:68:2f:2d:b3:e2:75:14:
69:a4:03:b9:d0:26:db:ab:e7:93:5a:5f:26:19:e6:
a6:94:98:5e:b3

prime2:

00:c7:7e:d7:da:f8:68:4c:be:f6:3a:fc:90:69:e7:
31:b0:c8:92:d5:a6:b9:5b:28:99:73:92:18:f5:21:
bc:a6:4d:6a:4d:69:2f:c6:8a:83:17:50:12:c2:90:
56:3c:53:60:ec:14:70:2d:6e:25:e7:7f:c8:ec:8a:
02:8b:a3:a7:f7

exponent1:

00:90:69:87:53:de:dd:a9:55:5f:1c:64:8f:0f:a1:
46:c7:71:ea:e3:5d:eb:70:d3:c7:eb:aa:08:32:6d:
d9:21:d5:f4:62:46:c6:87:e8:f2:9d:e8:03:87:28:
18:8a:57:73:e1:25:45:61:cb:ac:9e:25:b0:56:fe:
97:fb:2c:49:a3

exponent2:

72:68:c3:40:58:2f:f0:bb:9f:99:0a:99:83:b2:cc:
63:97:53:e6:65:9c:6d:b4:25:f5:0a:b7:77:75:e6:
ab:f5:3f:de:af:51:6f:ea:a5:ed:ab:c5:66:81:c2:
83:0c:4a:4c:7a:18:2f:65:b9:25:3f:ba:aa:35:27:
ea:8f:6b:d7

coefficient:

00:82:ed:14:6e:f6:dd:ca:20:92:4a:79:ac:84:e4:
8b:e4:34:fe:01:a8:22:59:41:ef:03:9b:bf:05:93:
ac:c1:a6:1b:7f:4d:c0:d6:fd:94:fe:9f:7e:cf:3a:
c2:72:06:6f:fa:0c:93:e9:19:9c:b6:e9:bf:be:83:
4e:e1:7f:90:57

openssl rsa 提取公钥中的模数(modulus)n

openssl rsa -in rsa_private_crypt1.pem -passin pass:123456  -pubout -out rsa_public.pem

writing RSA key

cat rsa_public.pem 

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpP8Kf7qB6A8bW75FwQF2FWkbN
bMwhaVcNKJUBV2B45FMPcDboQzuYb+mWG+/41hL5eHBguI5JHL2weftkZpaeG5rH
4S2Uhq3CHGjaDjwaAIDG4gA/ZL/0fw6Y8852qYgVjqyBCfvXEwDKuHBtJszCqW+M
E5qAtgpodMl+QcMjtQIDAQAB
-----END PUBLIC KEY-----

openssl rsa -in rsa_private_crypt1.pem -passin pass:123456  -noout -modulus 

Modulus=A93FC29FEEA07A03C6D6EF9170405D855A46CD6CCC2169570D289501576078E4530F7036E8433B986FE9961BEFF8D612F9787060B88E491CBDB079FB6466969E1B9AC7E12D9486ADC21C68DA0E3C1A0080C6E2003F64BFF47F0E98F3CE76A988158EAC8109FBD71300CAB8706D26CCC2A96F8C139A80B60A6874C97E41C323B5

openssl rsa 把pem格式转化成der格式

openssl rsa -in rsa_private_crypt1.pem -passin pass:123456 -outform der -aes256 -passout pass:123456 -out rsa_private_crypt.der

writing RSA key

hexdump -e '16/1 "%02X " "\n"' rsa_private_crypt.der 

30 82 02 5E 02 01 00 02 81 81 00 A9 3F C2 9F EE
A0 7A 03 C6 D6 EF 91 70 40 5D 85 5A 46 CD 6C CC
21 69 57 0D 28 95 01 57 60 78 E4 53 0F 70 36 E8
43 3B 98 6F E9 96 1B EF F8 D6 12 F9 78 70 60 B8
8E 49 1C BD B0 79 FB 64 66 96 9E 1B 9A C7 E1 2D
94 86 AD C2 1C 68 DA 0E 3C 1A 00 80 C6 E2 00 3F
64 BF F4 7F 0E 98 F3 CE 76 A9 88 15 8E AC 81 09
FB D7 13 00 CA B8 70 6D 26 CC C2 A9 6F 8C 13 9A
80 B6 0A 68 74 C9 7E 41 C3 23 B5 02 03 01 00 01
02 81 81 00 98 C7 24 91 A8 1F 96 64 C8 5E 07 1D
BB 3D 6E EE 4C 82 B3 36 ED C1 8F 4C 74 65 58 B4
40 42 A6 7E 3C F8 27 AC 78 D7 0F F9 D7 B3 FC BB
B5 50 63 35 F7 9A 1F 9F 3F 99 B9 27 C7 97 81 55
4B D9 4B 45 6F B4 B1 E0 AE B5 2F 21 99 1F 35 EB
92 2A 6A 52 E4 BF 10 C9 85 26 FC D0 E7 A0 D7 8E
CE D7 8C CC 82 E2 71 60 62 0D D7 FA 55 AC 08 BA
52 EC D2 92 46 8E 2F 18 2A 33 FD 8A AC EF CE 22
7E 44 70 15 02 41 00 D9 2F C9 F0 17 B2 14 67 23
5B B2 13 48 20 0A F4 60 CD F9 1B 9F 1B 96 B4 FE
B6 92 63 30 20 E5 6F 70 0C 79 0B D4 68 2F 2D B3
E2 75 14 69 A4 03 B9 D0 26 DB AB E7 93 5A 5F 26
19 E6 A6 94 98 5E B3 02 41 00 C7 7E D7 DA F8 68
4C BE F6 3A FC 90 69 E7 31 B0 C8 92 D5 A6 B9 5B
28 99 73 92 18 F5 21 BC A6 4D 6A 4D 69 2F C6 8A
83 17 50 12 C2 90 56 3C 53 60 EC 14 70 2D 6E 25
E7 7F C8 EC 8A 02 8B A3 A7 F7 02 41 00 90 69 87
53 DE DD A9 55 5F 1C 64 8F 0F A1 46 C7 71 EA E3
5D EB 70 D3 C7 EB AA 08 32 6D D9 21 D5 F4 62 46
C6 87 E8 F2 9D E8 03 87 28 18 8A 57 73 E1 25 45
61 CB AC 9E 25 B0 56 FE 97 FB 2C 49 A3 02 40 72
68 C3 40 58 2F F0 BB 9F 99 0A 99 83 B2 CC 63 97
53 E6 65 9C 6D B4 25 F5 0A B7 77 75 E6 AB F5 3F
DE AF 51 6F EA A5 ED AB C5 66 81 C2 83 0C 4A 4C
7A 18 2F 65 B9 25 3F BA AA 35 27 EA 8F 6B D7 02
41 00 82 ED 14 6E F6 DD CA 20 92 4A 79 AC 84 E4
8B E4 34 FE 01 A8 22 59 41 EF 03 9B BF 05 93 AC
C1 A6 1B 7F 4D C0 D6 FD 94 FE 9F 7E CF 3A C2 72
06 6F FA 0C 93 E9 19 9C B6 E9 BF BE 83 4E E1 7F
90 57

openssl rsa -inform der -in rsa_private.der -passin pass:123456 -modulus -noout

Modulus=B14A838D93A20FDEA12C68C883E43810EAE5A7482BC58D62D38ED485799D8159FCEF16E9BB13EFE323172DFBE9321001ABEFE9B76804099BCF23750404006F31

openssl rsa 把der格式转化成pem格式

openssl rsa -inform der -in rsa_private.der -passin pass:123456 -out rsa.pem

writing RSA key

cat rsa.pem

-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALFKg42Tog/eoSxoyIPkOBDq5adIK8WNYtOO1IV5nYFZ/O8W6bsT
7+MjFy376TIQAavv6bdoBAmbzyN1BAQAbzECAwEAAQJAPz9p9xP3+NlffkxTXFoK
dl6WVzs0AmISI48M2iEsw3wS6auo09AUKg70mP0ueU/GlKsVY78CFzSfSLJUtGdY
AQIhANmovwa/oL5/HwS/IHRNk1r2ZvUnfPTc90zCW9EqZxFhAiEA0IViaiYkqcSw
IC+uTn+B87OLvGHLX4UKk1CRoMwQn9ECIEJPdJMbwl8G325UxBBqqd/mfYtmkl0P
DJBoDgz1PB1BAiEAtLdE6bYRBEkAU4S/TizXlTvAGQ2wUiJdXfrvmyoAJmECIBkM
IODUSpheA8692yYgQ6t/m8EEIVd2I7K8ebZXr2sc
-----END RSA PRIVATE KEY-----

openssl rsautl指令说明

rsautl用于密钥交换和数字签名。实质上就是使用RSA公钥或者私钥加密。

而无论是使用公钥加密还是私钥加密,RSA每次能够加密的数据长度不能超过RSA密钥长度,并且根据具体的补齐方式不同输入的加密数据最大长度也不一样,而输出长度则总是跟RSA密钥长度相等。RSA不同的补齐方法对应的输入输入长度如下表

数据补齐方式输入数据长度输出数据长度参数字符串
PKCS#1 v1.5少于(密钥长度-11)字节同密钥长度-pkcs
PKCS#1 OAEP少于(密钥长度-11)字节同密钥长度-oaep
PKCS#1 for SSLv23少于(密钥长度-11)字节同密钥长度-ssl
不使用补齐同密钥长度同密钥长度-raw
openssl help rsautl

Usage: rsautl [options]
Valid options are:
-help Display this summary
-in infile Input file
-out outfile Output file
-inkey val Input key
-keyform PEM|DER|ENGINE Private key format - default PEM
-pubin Input is an RSA public
-certin Input is a cert carrying an RSA public key
-ssl Use SSL v2 padding
-raw Use no padding
-pkcs Use PKCS#1 v1.5 padding (default)
-oaep Use PKCS#1 OAEP
-sign Sign with private key
-verify Verify with public key
-asn1parse Run output through asn1parse; useful with -verify
-hexdump Hex dump output
-x931 Use ANSI X9.31 padding
-rev Reverse the order of the input buffer
-encrypt Encrypt with public key
-decrypt Decrypt with private key
-passin val Input file pass phrase source
-rand val Load the file(s) into the random number generator
-writerand outfile Write random data to the specified file
-engine val Use engine, possibly a hardware device

使用rsautl进行加密和解密操作
/使用RSA密钥作为密钥进行加密,实际上使用其中的公钥进行加密/

openssl rsautl -encrypt -in plain.txt -inkey rsa_private_crypt1.pem -passin pass:123456 -out enc.txt

cat enc.txt |hexdump -C

00000000 12 41 bc 42 f3 b1 16 b4 3a 01 bb 11 ac f2 54 eb |.A.B....:.....T.|
00000010 4b 21 c5 64 65 df ac 80 28 7d 42 26 cc 6b 44 8f |K!.de...(}B&.kD.|
00000020 5d b0 00 1c 81 6f 77 14 5c 4c 7a 37 52 b6 c3 c7 |]....ow.Lz7R...|
00000030 39 df 16 12 58 a8 3b 1e 16 19 7e 57 80 94 43 b2 |9...X.;...~W..C.|
00000040 b5 7e 27 01 52 fc ef 46 53 a4 bc 98 1e a5 3e a0 |.~'.R..FS.....>.|
00000050 99 8f dd 52 bd e2 91 e8 7d 45 16 eb 2e 26 e5 42 |...R....}E...&.B|
00000060 e4 56 6f df e5 23 3d 88 55 f5 c2 c5 1b 2b 6f a3 |.Vo..#=.U....+o.|
00000070 9e a7 e2 fd ef c2 09 0b a5 e7 26 9b 88 64 c4 7e |..........&..d.~|
00000080
/使用RSA密钥作为密钥进行解密,实际上使用其中的私钥进行解密/

openssl rsautl -decrypt -in enc.txt -inkey rsa_private_crypt1.pem -passin pass:123456  -out plain1.txt

/使用公钥进行加密/

openssl rsautl -encrypt -in plain.txt -inkey rsa_public.pem -pubin -out enc1.txt

/使用RSA作为密钥进行解密,实际上使用其中的私钥进行解密/

openssl rsautl -decrypt -in enc1.txt -inkey rsa_private_crypt1.pem -passin pass:123456  -out plain1.txt

注意:相同的明文,使用密钥加密和公钥加密后的密文结果不一样?是因为rsa公钥加密的时候根据填充模式填充随机数,导致每次加密结果不同。默认使用的填充方式为-pkcs,可以使用-oaep -ssl -raw

使用rsautl进行签名和验证操作
/使用RSA密钥进行签名,实际上使用私钥进行加密/

openssl rsautl -sign -in plain.txt -inkey rsa_private_crypt1.pem -passin pass:123456 -out sign.txt

/使用RSA密钥进行验证,实际上使用公钥进行解密/

openssl rsautl -verify -in sign.txt -inkey rsa_private_crypt1.pem -passin pass:123456 -out replain.txt

当然也可以像上面的示例,只使用公钥,加上 -pubin 参数就可以了。

openssl PKCS 格式转换 命令
rsa private key To convert from PKCS#1 to PKCS#8:

openssl pkcs8 -topk8 -inform pem -in private_pkcs1.pem -outform pem -nocrypt -out private_pkcs8.pem

rsa private key To convert from PKCS#8 to PKCS#1:

openssl rsa -in private_pkcs8.pem -out private_pkcs1.pem

rsa public key To convert from PKCS#8 to PKCS#1:

openssl rsa -pubin -in public_pkcs8.pem -RSAPublicKey_out -out public_pkcs1.pem

rsa public key To convert from PKCS#1 to PKCS#8:

openssl rsa -RSAPublicKey_in -in public_pkcs1.pem -pubout -out public_pkcs8.pem

RSA私钥和公钥文件格式 (pkcs#1, pkcs#8)

RSA Public Key file (PKCS#1)
The RSA Public key PEM file is specific for RSA keys.

It starts and ends with the tags:

-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----

Within the base64 encoded data the following DER structure is present:

RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}

Public Key file (PKCS#8)
Because RSA is not used exclusively inside X509 and SSL/TLS, a more generic key format is available in the form of PKCS#8, that identifies the type of public key and contains the relevant data.

It starts and ends with the tags:

-----BEGIN PUBLIC KEY-----
BASE64 ENCODED DATA
-----END PUBLIC KEY-----

Within the base64 encoded data the following DER structure is present:

PublicKeyInfo ::= SEQUENCE {
  algorithm       AlgorithmIdentifier,
  PublicKey       BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
  algorithm       OBJECT IDENTIFIER,
  parameters      ANY DEFINED BY algorithm OPTIONAL
}

So for an RSA public key, the OID is 1.2.840.113549.1.1.1 and there is a RSAPublicKey as the PublicKey key data bitstring.

RSA Private Key file (PKCS#1)
The RSA private key PEM file is specific for RSA keys.

It starts and ends with the tags:

-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----

Within the base64 encoded data the following DER structure is present:

RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

Private Key file (PKCS#8)
Because RSA is not used exclusively inside X509 and SSL/TLS, a more generic key format is available in the form of PKCS#8, that identifies the type of private key and contains the relevant data.

The unencrypted PKCS#8 encoded data starts and ends with the tags:

-----BEGIN PRIVATE KEY-----
BASE64 ENCODED DATA
-----END PRIVATE KEY-----

Within the base64 encoded data the following DER structure is present:

PrivateKeyInfo ::= SEQUENCE {
  version         Version,
  algorithm       AlgorithmIdentifier,
  PrivateKey      BIT STRING
}
 
AlgorithmIdentifier ::= SEQUENCE {
  algorithm       OBJECT IDENTIFIER,
  parameters      ANY DEFINED BY algorithm OPTIONAL
}

So for an RSA private key, the OID is 1.2.840.113549.1.1.1 and there is a RSAPrivateKey as the PrivateKey key data bitstring.

The encrypted PKCS#8 encoded data start and ends with the tags:

-----BEGIN ENCRYPTED PRIVATE KEY-----
BASE64 ENCODED DATA
-----END ENCRYPTED PRIVATE KEY-----

Within the base64 encoded data the following DER structure is present:

EncryptedPrivateKeyInfo ::= SEQUENCE {
  encryptionAlgorithm  EncryptionAlgorithmIdentifier,
  encryptedData        EncryptedData
}
 
EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
 
EncryptedData ::= OCTET STRING

The EncryptedData OCTET STRING is a PKCS#8 PrivateKeyInfo (see above).

OpenSSL3.0.1下载

wget https://www.openssl.org/source/openssl-3.0.1.tar.gz

OpenSSL3.0.1解压

tar xvf openssl-3.0.1.tar.gz

OpenSSL3.0.1编译

cd openssl-3.0.1
./Configure --prefix=/opt/openssl --openssldir=/usr/local/ssl
make -j12
sudo make install
sudo ln -s /opt/openssl/bin/openssl /usr/bin/openssl3
echo "/opt/openssl/lib64/" > /etc/ld.so.conf.d/openssl3.0.1.conf
sudo ldconfig

OpenSSL3.0.1运行

openssl3 version
OpenSSL 3.0.1 14 Dec 2021 (Library: OpenSSL 3.0.1 14 Dec 2021)

OpenSSL3.0.1 生成 SM2 密钥

openssl3 ecparam -genkey -name SM2 
-----BEGIN SM2 PARAMETERS-----
BggqgRzPVQGCLQ==
-----END SM2 PARAMETERS-----
-----BEGIN PRIVATE KEY-----
MIGIAgEAMBQGCCqBHM9VAYItBggqgRzPVQGCLQRtMGsCAQEEIL/8jEekBeJc8bjW
96Xd0xiyqlE8cpq5wdr2GTRog4C+oUQDQgAE45bfR1XYxfUN8cc0gHzRQfbfbxXB
sb6eGcWHhhofO9c4smNcqsioh0aZCrD5e0iNKE/wpNQ5ySP7xiz7RlpxEQ==
-----END PRIVATE KEY-----

openssl3 ecparam -genkey -name SM2 -out sm2.key

OpenSSL3.0.1 命令行SM2签名

openssl3 pkeyutl -sign -inkey sm2.key -in test.data -rawin  -digest sm3 -pkeyopt distid:1234567812345678 |hexdump -C

00000000 30 44 02 20 26 c4 c1 3f 0f 45 01 eb 1c 42 03 c7 |0D. &..?.E...B..|
00000010 3d 82 2b c9 70 84 94 af ba 69 8f 4a 61 32 b6 98 |=.+.p....i.Ja2..|
00000020 72 b1 d2 91 02 20 5f 02 75 9c 0a 13 0e bb a7 4d |r.... _.u......M|
00000030 b6 64 4d 37 63 6b 75 df 83 ab 40 90 e7 7f a9 d0 |.dM7cku...@.....|
00000040 8e 80 5a 87 32 84 |..Z.2.|
00000046