openssl 非对称加密算法RSA命令详解

来源:互联网 发布:手机淘宝类目怎么写 编辑:程序博客网 时间:2024/06/05 18:47

1、非对称加密算法概述

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

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

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

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

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

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

一. 对称与非对称加密

采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。需要对加密和解密使用相同密钥的加密算法。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。对称性加密也称为密钥加密。对于加密和解密都使用同一个密钥,如何把密钥安全地传递到解密者手上就成了必须要解决的问题。常用的对称加密算法有:DES、IDEA、RC2、RC4、SKIPJACK、RC5、AES算法等

与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

非对称加密算法的保密性比较好,它消除了最终用户交换密钥的需要,但加密和解密花费时间长、速度慢,它不适合于对文件加密而只适用于对少量数据进行加密。经典的非对称加密算法如RSA算法等安全性都相当高。非对称加密的典型应用是数字信封、数字签名。

二. 数字信封与数字签名

数字信封(Digital Envelope)是将对称密钥通过非对称加密(即:有公钥和私钥两个)的结果分发对称密钥的方法。数字信封是公钥密码体制在实际中的一个应用,是用加密技术来保证只有规定的特定收信人才能阅读通信的内容。

PKCS#7对数字信封的定义:数字信封包含被加密的内容和被加密的用于加密该内容的密钥。虽然经常使用接收方的公钥来加密“加密密钥”,但这并不是必须的,也可以使用发送方和接收方预共享的对称密钥来加密。当接收方收到数字信封时,先用私钥或预共享密钥解密,得到“加密密钥”,再用该密钥解密密文,获得原文。数字信封技术使用两层加密体系。

在数字信封中,信息发送方采用对称密钥来加密信息内容,然后将此对称密钥用接收方的公开密钥来加密(这部分称数字信封)之后,将它和加密后的信息一起发送给接收方,接收方先用相应的私有密钥打开数字信封,得到对称密钥,然后使用对称密钥解开加密信息。数字信封主要包括数字信封打包和数字信封拆解,数字信封打包是使用对方的公钥将加密密钥进行加密的过程,只有对方的私钥才能将加密后的数据(通信密钥)还原;数字信封拆解是使用私钥将加密过的数据解密的过程。

数字签名(Digital Signature,又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。

数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。数字签名是个加密的过程,数字签名验证是个解密的过程。

数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。

三. RSA加密算法

RSA公开密钥密码体制。所谓的公开密钥密码体制就是使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。

在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然秘密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK。

RSA的算法涉及三个参数,n、e1、e2。
其中,n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质;再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。(n,e1),(n,e2)就是密钥对。其中(n,e1)为公钥,(n,e2)为私钥。

RSA加解密的算法完全相同,设A为明文,B为密文,则:A=B^e2 mod n;B=A^e1 mod n;(公钥加密体制中,一般用公钥加密,私钥解密)e1和e2可以互换使用,即:A=B^e1 mod n;B=A^e2 mod n;

这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。


openssl支持以上三种算法,并为三种算法提供了丰富的指令集,本章主要介绍RSA算法及相关指令

2、RSA算法相关指令及用法

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

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

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

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

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

2.1 genrsa指令说明

genrsa用于生成密钥对,其用法如下

复制代码
xlzh@cmos:~$ openssl genrsa -usage: genrsa [args] [numbits]                                                     //密钥位数,建议1024及以上 -des            encrypt the generated key with DES in cbc mode                    //生成的密钥使用des方式进行加密 -des3           encrypt the generated key with DES in ede cbc mode (168 bit key)  //生成的密钥使用des3方式进行加密 -seed                 encrypt PEM output with cbc seed                                  //生成的密钥还是要seed方式进行 -aes128, -aes192, -aes256                 encrypt PEM output with cbc aes                                   //生成的密钥使用aes方式进行加密 -camellia128, -camellia192, –camellia256                  encrypt PEM output with cbc camellia                              //生成的密钥使用camellia方式进行加密 -out file       output the key to 'file                                           //生成的密钥文件,可从中提取公钥 -passout arg    output file pass phrase source                                    //指定密钥文件的加密口令,可从文件、环境变量、终端等输入 -f4             use F4 (0x10001) for the E value                                  //选择指数e的值,默认指定该项,e值为65537 -3              use 3 for the E value                                             //选择指数e的值,默认值为65537,使用该选项则指数指定为3 -engine e       use engine e, possibly a hardware device.                         //指定三方加密库或者硬件 -rand file:file:...                 load the file (or the files in the directory) into                //产生随机数的种子文件                 the random number generator
复制代码

可以看到genrsa指令使用较为简单,常用的也就有指定加密算法、输出密钥文件、加密口令。我们仅举一个例子来说明

复制代码
/* * 指定密钥文件rsa.pem * 指定加密算法aes128 * 指定加密密钥123456 * 指定密钥长度1024 **/xlzh@cmos:~$ openssl genrsa -out rsa.pem -aes128 -passout pass:123456 1024Generating RSA private key, 1024 bit long modulus...............................................................++++++.................................++++++e is 65537 (0x10001)  // 默认模式65537/*加密后的密钥文件有加密算法等信息*/xlzh@cmos:~$ cat rsa.pem -----BEGIN RSA PRIVATE KEY-----Proc-Type: 4,ENCRYPTEDDEK-Info: AES-128-CBC,4C23682B0D34D339ED7E44819A70B4F9c9uHqqWbkcw3hjdQ/6fGuJcOFchd4+KfVZoJnnISnJBAhv3CelFAksKb2RKa5GoC4Eq6SykCCSH8OboPoPBjd1ZdAsDl1Pio0vIJfAoQ4NmaRJ61+6onJ/HAx2NFTDjNyrmsGOWejB6A3MT4KiXrvICnkKMsUY1Qp6ln2qOeVynmxeWAWiVZnjfm0OkScL1KRGSuL32vecN5b1S8fZTYJTS3PQxjmyaw65zLX+8mUObanL9WhSLTz2eo/6xTzRbDiOGMolfP/3ObqIAS3007qV48CtwWrlAa+RpbMVIiESN7BforOaNbh0s5NVuUnXYshx90iZj2M1L4i5SP8jKBunXPK6CHQtUQXpMH06nhoMNyZPtRQegFgZlwVOpOfoS5khGAjJPnEXI7ah8oCNYO21JV6SlMFxK1lUeS3xCvM8Cd/zVBSzD7jg+axBJr+LpOrhpmEFkStXHtFo3OK3BoyQHIzYEYH4S59xWO+dfrb2zUvkKsQKkV+TFMSZpr7b7UiegUcK3NrbcWDApfTYmf/edublJBv816to+hYQLhXKfuzP5iMJmjnubhrXrA6S477XN6nil9DGWzUEMPnH6Brc8mj7JwFtxdpWDN2pY+VcJ04O98fO08c+4eSS3u0Y9fTyxYy1C9nIWxF+t2Dulq94N4AQ2uyTXoVNhrmDYrJ9BUCugg6zx6xtU24aSGFvtnikgAU8JCX0GkcwU60tTLSxPNAWhNxJSJ5n7BXaV6QQ1GOiiKQlJAcRv2PMxNqVgKpoVq742+awsichrwqE5VIFW9AdSMyIT7w06IogyUrS+0+FmFS6qPtT3ZbFZakzkd-----END RSA PRIVATE KEY-----xlzh@cmos:~$
复制代码

2.2 rsa指令说明

rsa指令用户管理生成的密钥,其用法如下

复制代码
xlzh@cmos:~$ openssl rsa -unknown option -rsa [options] <infile >outfile     where options are -inform arg     input format - one of DER NET PEM                      //输入文件格式,默认pem格式 -outform arg    output format - one of DER NET PEM                     //输入文件格式,默认pem格式 -in arg         input file                                             //输入文件 -sgckey         Use IIS SGC key format                                 //指定SGC编码格式,兼容老版本,不应再使用 -passin arg     input file pass phrase source                          //指定输入文件的加密口令,可来自文件、终端、环境变量等 -out arg        output file                                            //输出文件 -passout arg    output file pass phrase source                         //指定输出文件的加密口令,可来自文件、终端、环境变量等 -des            encrypt PEM output with cbc des                        //使用des加密输出的文件 -des3           encrypt PEM output with ede cbc des using 168 bit key  //使用des3加密输出的文件 -seed           encrypt PEM output with cbc seed                       //使用seed加密输出的文件 -aes128, -aes192, -aes256                 encrypt PEM output with cbc aes                        //使用aes加密输出的文件 -camellia128, -camellia192, -camellia256                 encrypt PEM output with cbc camellia                   //使用camellia加密输出的文件呢 -text           print the key in text                                  //以明文形式输出各个参数值 -noout          don't print key out                                    //不输出密钥到任何文件 -modulus        print the RSA key modulus                              //输出模数指 -check          verify key consistency                                 //检查输入密钥的正确性和一致性 -pubin          expect a public key in input file                      //指定输入文件是公钥 -pubout         output a public key                                    //指定输出文件是公钥 -engine e       use engine e, possibly a hardware device.              //指定三方加密库或者硬件xlzh@cmos:~$
复制代码

rsa指令操作示例如下

1、rsa添加和去除密钥的保护口令

复制代码
/*生成不加密的RSA密钥*/xlzh@cmos:~/test$ openssl genrsa -out RSA.pemGenerating RSA private key, 512 bit long modulus..............++++++++++++.....++++++++++++e is 65537 (0x10001)/*为RSA密钥增加口令保护*/xlzh@cmos:~/test$ openssl rsa -in RSA.pem -des3 -passout pass:123456 -out E_RSA.pemwriting RSA key/*为RSA密钥去除口令保护*/xlzh@cmos:~/test$ openssl rsa -in E_RSA.pem -passin pass:123456 -out P_RSA.pemwriting RSA key/*比较原始后的RSA密钥和去除口令后的RSA密钥,是一样*/xlzh@cmos:~/test$ diff RSA.pem P_RSA.pem
复制代码

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

复制代码
/*生成RSA密钥*/xlzh@cmos:~/test$ openssl genrsa -des3 -passout pass:123456 -out RSA.pemGenerating RSA private key, 512 bit long modulus..................++++++++++++......................++++++++++++e is 65537 (0x10001)/*修改加密算法为aes128,口令是123456*/xlzh@cmos:~/test$ openssl rsa -in RSA.pem -passin pass:123456 -aes128 -passout pass:123456 -out E_RSA.pemwriting RSA key
复制代码

3、查看密钥对中的各个参数

xlzh@cmos:~/test$ openssl rsa -in RSA.pem -des -passin pass:123456 -text -noout

4、提取密钥中的公钥并打印模数值

/*提取公钥,用pubout参数指定输出为公钥*/xlzh@cmos:~/test$ openssl rsa -in RSA.pem -passin pass:123456 -pubout -out pub.pemwriting RSA key/*打印公钥中模数值*/xlzh@cmos:~/test$ openssl rsa -in pub.pem -pubin -modulus -nooutModulus=C35E0B54041D78466EAE7DE67C1DA4D26575BC1608CE6A199012E11D10ED36E2F7C651D4D8B40D93691D901E2CF4E21687E912B77DCCE069373A7F6585E946EF

5、转换密钥的格式

/*把pem格式转化成der格式,使用outform指定der格式*/xlzh@cmos:~/test$ openssl rsa -in RSA.pem -passin pass:123456 -des -passout pass:123456 -outform der -out rsa.derwriting RSA key/*把der格式转化成pem格式,使用inform指定der格式*/xlzh@cmos:~/test$ openssl rsa -in rsa.der -inform der -passin pass:123456 -out rsa.pem

2.3 rsautl指令说明

上述两个指令是密钥的生成及管理作用,rsautl则是真正用于密钥交换和数字签名。实质上就是使用RSA公钥或者私钥加密。

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

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

rsautl指令用法如下

复制代码
xlzh@cmos:~$ openssl rsautl - Usage: rsautl [options]                  -in file        input file                                           //输入文件-out file       output file                                          //输出文件-inkey file     input key                                            //输入的密钥-keyform arg    private key format - default PEM                     //指定密钥格式-pubin          input is an RSA public                               //指定输入的是RSA公钥-certin         input is a certificate carrying an RSA public key    //指定输入的是证书文件-ssl            use SSL v2 padding                                   //使用SSLv23的填充方式-raw            use no padding                                       //不进行填充-pkcs           use PKCS#1 v1.5 padding (default)                    //使用V1.5的填充方式-oaep           use PKCS#1 OAEP                                      //使用OAEP的填充方式-sign           sign with private key                                //使用私钥做签名-verify         verify with public key                               //使用公钥认证签名-encrypt        encrypt with public key                              //使用公钥加密-decrypt        decrypt with private key                             //使用私钥解密-hexdump        hex dump output                                      //以16进制dump输出-engine e       use engine e, possibly a hardware device.            //指定三方库或者硬件设备-passin arg    pass phrase source                                    //指定输入的密码
复制代码

rsautl操作示例如下:

1、使用rsautl进行加密和解密操作

复制代码
/*生成RSA密钥*/xlzh@cmos:~/test$ openssl genrsa -des3 -passout pass:123456 -out RSA.pem Generating RSA private key, 512 bit long modulus............++++++++++++...++++++++++++e is 65537 (0x10001)/*提取公钥*/xlzh@cmos:~/test$ openssl rsa -in RSA.pem -passin pass:123456 -pubout -out pub.pem writing RSA key/*使用RSA作为密钥进行加密,实际上使用其中的公钥进行加密*/xlzh@cmos:~/test$ openssl rsautl -encrypt -in plain.txt -inkey RSA.pem -passin pass:123456 -out enc.txt/*使用RSA作为密钥进行解密,实际上使用其中的私钥进行解密*/xlzh@cmos:~/test$ openssl rsautl -decrypt -in enc.txt -inkey RSA.pem -passin pass:123456 -out replain.txt/*比较原始文件和解密后文件*/xlzh@cmos:~/test$ diff plain.txt replain.txt /*使用公钥进行加密*/xlzh@cmos:~/test$ openssl rsautl -encrypt -in plain.txt -inkey pub.pem -pubin -out enc1.txt/*使用RSA作为密钥进行解密,实际上使用其中的私钥进行解密*/xlzh@cmos:~/test$ openssl rsautl -decrypt -in enc1.txt -inkey RSA.pem -passin pass:123456 -out replain1.txt/*比较原始文件和解密后文件*/xlzh@cmos:~/test$ diff plain.txt replain1.txt
复制代码

在进行这个实验的时候有个疑惑,为什么相同的明文,使用密钥加密和公钥加密后的密文结果不一样?在网上查询了下,是因为rsa公钥加密的时候根据填充模式填充随机数,导致每次加密结果不同。

2、使用rsautl进行签名和验证操作

复制代码
/*提取PCKS8格式的私钥*/xlzh@cmos:~/test$ openssl pkcs8 -topk8 -in RSA.pem -passin pass:123456 -out pri.pem -nocrypt/*使用RSA密钥进行签名,实际上使用私钥进行加密*/xlzh@cmos:~/test$ openssl rsautl -sign -in plain.txt -inkey RSA.pem -passin pass:123456 -out sign.txt/*使用RSA密钥进行验证,实际上使用公钥进行解密*/xlzh@cmos:~/test$ openssl rsautl -verify -in sign.txt -inkey RSA.pem -passin pass:123456 -out replain.txt/*对比原始文件和签名解密后的文件*/xlzh@cmos:~/test$ diff plain.txt replain.txt /*使用私钥进行签名*/xlzh@cmos:~/test$ openssl rsautl -sign -in plain.txt -inkey pri.pem  -out sign1.txt/*使用公钥进行验证*/xlzh@cmos:~/test$ openssl rsautl -verify -in sign1.txt -inkey pub.pem -pubin -out replain1.txt/*对比原始文件和签名解密后的文件*/xlzh@cmos:~/test$ cat plain replain1.txt
复制代码

要注意这里的签名和验证过程其本质上是加解密操作,不是标准意义上的签名和验证。标准意义上签名和验证是需要增加摘要操作的,后续文章再详细阐述。

一. RSA PEM文件格式

1. PEM私钥格式文件

1
2
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----

2. PEM公钥格式文件

1
2
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----

3. PEM RSAPublicKey公钥格式文件

1
2
-----BEGIN RSA PUBLIC KEY-----
-----END RSA PUBLIC KEY-----

二. OpenSSL密钥相关命令

1. 生成密钥

1
2
3
openssl genrsa -out key.pem 1024
    -out 指定生成文件,此文件包含公钥和私钥两部分,所以即可以加密,也可以解密
    1024 生成密钥的长度

2. 提取PEM格式公钥

1
2
3
openssl rsa -in key.pem -pubout -out pubkey.pem
    -in 指定输入的密钥文件
    -out 指定提取生成公钥的文件(PEM公钥格式)

3. 提取PEM RSAPublicKey格式公钥

1
2
3
openssl rsa -in key.pem -RSAPublicKey_out -out pubkey.pem
    -in 指定输入的密钥文件
    -out 指定提取生成公钥的文件(PEM RSAPublicKey格式)

4. 公钥加密文件

1
2
3
4
5
openssl rsautl -encrypt -in input.file -inkey pubkey.pem -pubin -out output.file
    -in 指定被加密的文件
    -inkey 指定加密公钥文件
    -pubin 表面是用纯公钥文件加密
    -out 指定加密后的文件

5. 私钥解密文件

1
2
3
4
openssl rsautl -decrypt -in input.file -inkey key.pem -out output.file
    -in 指定需要解密的文件
    -inkey 指定私钥文件
    -out 指定解密后的文件

三. RSA相关API

1. 基本数据结构

1
2
3
4
5
6
7
8
9
10
11
struct{
    BIGNUM *n;              // public modulus
    BIGNUM *e;              // public exponent
    BIGNUM *d;              // private exponent
    BIGNUM *p;              // secret prime factor
    BIGNUM *q;              // secret prime factor
    BIGNUM *dmp1;           // d mod (p-1)
    BIGNUM *dmq1;           // d mod (q-1)
    BIGNUM *iqmp;           // q^-1 mod p
    // ...
} RSA;

2. BN大数系列函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//新生成一个BIGNUM结构
BIGNUM *BN_new(void);
 
//释放一个BIGNUM结构,释放完后a=NULL;
voidBN_free(BIGNUM *a);
 
//初始化所有项均为0,一般为BN_ init(&c)
voidBN_init(BIGNUM *);
 
//将a中所有项均赋值为0,但是内存并没有释放
voidBN_clear(BIGNUM *a);
 
//相当与将BN_free和BN_clear综合,要不就赋值0,要不就释放空间。
voidBN_clear_free(BIGNUM *a);
 
//设置大数a为整数w
intBN_set_word(BIGNUM *a, unsigned longw);
 
//假如大数a能表示为long型,那么返回一个long型数
unsignedlongBN_get_word(BIGNUM *a);
 
//产生一个加密用的强bits的伪随机数
//若top=-1,最高位为0,top=0,最高位为1,top=1,最高位和次高位为1,bottom为真,随机数为偶数
intBN_rand(BIGNUM *rnd, intbits,inttop,intbottom);
 
//将 a 转化为字符串存入to,to的空间必须大于BN_num_bytes(a)
intBN_bn2bin(constBIGNUM *a, unsigned char*to);
 
//将s中的len位的正整数转化为大数
BIGNUM *BN_bin2bn(constunsignedchar*s,intlen, BIGNUM *ret);
 
//将大数转化为16进制字符串
char*BN_bn2hex(constBIGNUM *a);
  
//将大数转化为10进制字符串
char*BN_bn2dec(constBIGNUM *a);
 
//将16进制字符串转成大数
intBN_hex2bn(BIGNUM **a, constchar*str);
 
//将10进制字符串传成大数
intBN_dec2bn(BIGNUM **a, constchar*str);

3. RSA系列函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//初始化一个RSA结构
RSA * RSA_new(void);
  
//释放一个RSA结构
voidRSA_free(RSA *rsa);
 
//RSA私钥产生函数
//产生一个模为num位的密钥对,e为公开的加密指数,一般为65537(0x10001)
RSA *RSA_generate_key(intnum, unsigned longe,void(*callback)(int,int,void*),void*cb_arg);
 
//判断位数函数, 返回RSA模的位数
intRSA_size(constRSA *rsa);
 
//测试p、q是否为素数
intRSA_check_key(RSA *rsa);

4. PEM系列函数

1
2
3
4
5
6
7
8
9
10
11
//从文件中加载RSAPublicKey格式公钥证书
RSA *PEM_read_RSAPublicKey(FILE*fp, RSA **x, pem_password_cb *cb, void*u);
 
//从BIO重加载RSAPublicKey格式公钥证书
RSA *PEM_read_bio_RSAPublicKey(BIO *bp, RSA **x, pem_password_cb *cb, void*u);
 
//输出RSAPublicKey公钥证书到文件
intPEM_write_RSAPublicKey(FILE*fp, RSA *x);
 
//输出RSAPublicKey公钥证书到BIO
intPEM_write_bio_RSAPublicKey(BIO *bp, RSA *x);

5. RSA加密API

1
2
3
4
5
6
7
intRSA_public_encrypt(intflen, unsigned char*from, unsigned char*to, RSA *rsa, intpadding);
 
参数说明:
    flen: 要加密信息长度
    from: 要加密信息
    to: 加密后的信息
    padding: 采取的加密方案, 分为: RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING, RSA_SSLV23_PADDING, RSA_NO_PADDING

6. RSA解密API

1
2
3
4
5
6
7
intRSA_private_decrypt(intflen, unsigned char*from, unsigned char*to, RSA *rsa, intpadding);
 
参数说明:
    flen: 要解密的信息长度
    from: 要解密的信息
    to: 解密后的信息
    padding: 采取的解密方案

四. RSA编程示例

1. 数据加、密解密示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<openssl/rsa.h>
#include<openssl/pem.h>
#include<openssl/err.h>
 
#define PRIKEY "prikey.pem"
#define PUBKEY "pubkey.pem"
#define BUFFSIZE 4096
 
/************************************************************************
 * RSA加密解密函数
 *
 * file: test_rsa_encdec.c
 * gcc -Wall -O2 -o test_rsa_encdec test_rsa_encdec.c -lcrypto -lssl
 *
 * author: tonglulin@gmail.com by www.qmailer.net
 ************************************************************************/
 
char*my_encrypt(char*str,char*pubkey_path)
{
    RSA *rsa = NULL;
    FILE*fp = NULL;
    char*en = NULL;
    intlen = 0;
    intrsa_len = 0;
 
    if((fp = fopen(pubkey_path,"r")) == NULL) {
        returnNULL;
    }
 
    /* 读取公钥PEM,PUBKEY格式PEM使用PEM_read_RSA_PUBKEY函数 */
    if((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) == NULL) {
        returnNULL;
    }
 
    RSA_print_fp(stdout, rsa, 0);
 
    len = strlen(str);
    rsa_len = RSA_size(rsa);
 
    en = (char*)malloc(rsa_len + 1);
    memset(en, 0, rsa_len + 1);
 
    if(RSA_public_encrypt(rsa_len, (unsigned char*)str, (unsigned char*)en, rsa, RSA_NO_PADDING) < 0) {
        returnNULL;
    }
 
    RSA_free(rsa);
    fclose(fp);
 
    returnen;
}
 
char*my_decrypt(char*str,char*prikey_path)
{
    RSA *rsa = NULL;
    FILE*fp = NULL;
    char*de = NULL;
    intrsa_len = 0;
 
    if((fp = fopen(prikey_path,"r")) == NULL) {
        returnNULL;
    }
 
    if((rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)) == NULL) {
        returnNULL;
    }
 
    RSA_print_fp(stdout, rsa, 0);
 
    rsa_len = RSA_size(rsa);
    de = (char*)malloc(rsa_len + 1);
    memset(de, 0, rsa_len + 1);
 
    if(RSA_private_decrypt(rsa_len, (unsigned char*)str, (unsigned char*)de, rsa, RSA_NO_PADDING) < 0) {
        returnNULL;
    }
 
    RSA_free(rsa);
    fclose(fp);
 
    returnde;
}
 
intmain(intargc,char*argv[])
{
    char*src = "hello, world!";
    char*en = NULL;
    char*de = NULL;
 
    printf("src is: %s\n", src);
 
    en = my_encrypt(src, PUBKEY);
    printf("enc is: %s\n", en);
 
    de= my_decrypt(en, PRIKEY);
    printf("dec is: %s\n", de);
 
    if(en != NULL) {
        free(en);
    }
 
    if(de != NULL) {
        free(de);
    }
 
    return0;
}

2. PEM/BIGNUM公钥转换示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <stdlib.h>
#include <string.h>
 
#include <openssl/rsa.h>
#include <openssl/pem.h>
 
/************************************************************************
 * RSA PEM/BIGNUM公钥转换函数
 *
 * file: test_rsa_pubkey.c
 * gcc -Wall -O2 -o test_rsa_pubkey test_rsa_pubkey.c -lcrypto -lssl
 *
 * author: tonglulin@gmail.com by www.qmailer.net
 ************************************************************************/
 
constchar*n = "C7301B330C4E123E4FA9F54F49121E8CE07974D8BFEF1D39EC9245D573D66E7FAC258F86E2B0816C6BA875F10673E655E6A8DF48DEFDDB655E253ED5A4A0FBAD50D68E91D0459F9F2377BB8CA1583E3F83C06343A5A1177C903F498A6D14015CC975522BE4446CD1EB87E88EF05A863AF0DD7C4D413CF603EDF4893EEC063BE3";
 
constchar*pubkey = "-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAMcwGzMMThI+T6n1T0kSHozgeXTYv+8dOeySRdVz1m5/rCWPhuKwgWxr\nqHXxBnPmVeao30je/dtlXiU+1aSg+61Q1o6R0EWfnyN3u4yhWD4/g8BjQ6WhF3yQ\nP0mKbRQBXMl1UivkRGzR64fojvBahjrw3XxNQTz2A+30iT7sBjvjAgMBAAE=\n-----END RSA PUBLIC KEY-----";
 
intmain(intargc,char*argv[])
{
    RSA    *rsa = NULL;
    BIO    *bio = NULL;
    BIGNUM *bne = NULL;
    BIGNUM *bnn = NULL;
    FILE*fp = NULL;
    unsignedlonge = 65537;
 
    if(argc < 2) {
        printf("%s pem|bignum args\n", argv[0]);
        return-1;
    }
 
    /* 将PEM转换为大数字符串 */
    if(strcasecmp(argv[1],"bignum") == 0) {
        if(argc == 3) {
            /* 从外部文件读 */
            fp = fopen(argv[2],"r");
            if(fp == NULL) {
                return-1;
            }
 
            rsa = PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL);
            if(rsa == NULL) {
                return-1;
            }
        }
        else{
            /* 从内存数据读 */
            bio = BIO_new(BIO_s_mem());
            BIO_puts(bio, pubkey);
 
            rsa = PEM_read_bio_RSAPublicKey(bio, &rsa, NULL, NULL);
            if(rsa == NULL) {
                return-1;
            }
        }
 
        RSA_print_fp(stdout, rsa, 0);
        printf("%s\n", BN_bn2hex(rsa->n));
        printf("%s\n", BN_bn2hex(rsa->e));
 
        if(argc == 3) {
            fclose(fp);
        }
        else{
            BIO_free(bio);
        }
        RSA_free(rsa);
    }
    /* 将大数字符串转换为PEM文件 */
    elseif(strcasecmp(argv[1],"pem") == 0) {
 
        bne = BN_new();
        if(bne == NULL) {
            return-1;
        }
 
        bnn = BN_new();
        if(bnn == NULL) {
            BN_free(bne);
            return-1;
        }
 
        rsa = RSA_new();
        if(rsa == NULL) {
            BN_free(bnn);
            BN_free(bne);
            return-1;
        }
 
        rsa->e = bne;
        rsa->n = bnn;
 
        /* 设置模数 */
        BN_set_word(bne, e);
        if(argc == 3) {
            BN_hex2bn(&bnn, argv[2]);
        }
        else{
            BN_hex2bn(&bnn, n);
        }
 
        PEM_write_RSAPublicKey(stdout, rsa);
 
        RSA_free(rsa);
    }
    else{
        return-1;
    }
 
    return0;
}

3. 密钥生成示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
 
/************************************************************************
 * RSA密钥生成函数
 *
 * file: test_rsa_genkey.c
 * gcc -Wall -O2 -o test_rsa_genkey test_rsa_genkey.c -lcrypto
 *
 * author: tonglulin@gmail.com by www.qmailer.net
 ************************************************************************/
intmain(intargc,char*argv[])
{
    /* 产生RSA密钥 */
    RSA *rsa = RSA_generate_key(1024, 65537, NULL, NULL);
 
    printf("BIGNUM: %s\n", BN_bn2hex(rsa->n));
 
    /* 提取私钥 */
    printf("PRIKEY:\n");
    PEM_write_RSAPrivateKey(stdout, rsa, NULL, NULL, 0, NULL, NULL);
 
    /* 提取公钥 */
    unsignedchar*n_b = (unsigned char*)calloc(RSA_size(rsa),sizeof(unsignedchar));
    unsignedchar*e_b = (unsigned char*)calloc(RSA_size(rsa),sizeof(unsignedchar));
 
    intn_size = BN_bn2bin(rsa->n, n_b);
    intb_size = BN_bn2bin(rsa->e, e_b);
 
    RSA *pubrsa = RSA_new();
    pubrsa->n = BN_bin2bn(n_b, n_size, NULL);
    pubrsa->e = BN_bin2bn(e_b, b_size, NULL);
 
    printf("PUBKEY: \n");
    PEM_write_RSAPublicKey(stdout, pubrsa);
 
    RSA_free(rsa);
    RSA_free(pubrsa);
 
    return0;
}








PKI常见证书格式和转换

PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准,PKCS 目前共发布过 15 个标准。

1.证书格式

PEM 格式

PEM格式通常用于数字证书认证机构(Certificate Authorities,CA),扩展名为.pem, .crt, .cer, and .key。 内容为Base64编码的ASCII码文件,有类似”—–BEGIN CERTIFICATE—–” 和 “—–END CERTIFICATE—–“的头尾标记。服务器认证证书,中级认证证书和私钥都可以储存为PEM格式(认证证书其实就是公钥)。Apache和类 似的服务器使用PEM格式证书。

DER 格式

DER格式与PEM不同之处在于其使用二进制而不是Base64编码的ASCII。扩展名为.der,但也经常使用.cer用作扩展名,所有类型的认证证书和私钥都可以存储为DER格式。Java使其典型使用平台。

PKCS#7/P7B 格式

PKCS#7 或 P7B格式通常以Base64的格式存储,扩展名为.p7b 或 .p7c, 有类似BEGIN PKCS7—–” 和 “—–END PKCS7—–“的头尾标记。PKCS#7 或 P7B只能存储认证证书或证书路径中的证书(就是存储认证证书链,本级,上级,到根级都存到一个文件中)。不能存储私钥,Windows和Tomcat都 支持这种格式。

PKCS#12/PFX 格式

PKCS#12 或 PFX格式是以加密的二进制形式存储服务器认证证书,中级认证证书和私钥。扩展名为.pfx 和 .p12,PXF通常用于Windows中导入导出认证证书和私钥。

 

2.转换方式

可以使用OpenSSL命令行工具在不同证书格式之间的转换

 

PEM to DER

openssl x509 -outform der -in certificate.pem -out certificate.der

PEM to P7B

openssl crl2pkcs7 -nocrl -certfile certificate.cer -out certificate.p7b -certfile CACert.cer

PEM to PFX

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt

DER to PEM

openssl x509 -inform der -in certificate.cer -out certificate.pem

P7B to PEM

openssl pkcs7 -print_certs -in certificate.p7b -out certificate.cer

PFX to PEM

openssl pkcs12 -in certificate.pfx -out certificate.cer -nodes

PXF转PEM后certificate.cer文件包含认证证书和私钥,需要把它们分开存储才能使用。

================================================================

PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准,PKCS 目前共发布过 15 个标准。 常用的有:

PKCS#7 Cryptographic Message Syntax Standard
PKCS#10 Certification Request Standard
PKCS#12 Personal Information Exchange Syntax Standard

X.509是常见通用的证书格式。所有的证书都符合为Public Key Infrastructure (PKI) 制定的 ITU-T X509 国际标准。

PKCS#7 常用的后缀是: .P7B .P7C .SPC
PKCS#12 常用的后缀有: .P12 .PFX
X.509 DER 编码(ASCII)的后缀是: .DER .CER .CRT
X.509 PAM 编码(Base64)的后缀是: .PEM .CER .CRT
.cer/.crt是用于存放证书,它是2进制形式存放的,不含私钥。
.pem跟crt/cer的区别是它以Ascii来表示。
pfx/p12用于存放个人证书/私钥,他通常包含保护密码,2进制方式
p10是证书请求
p7r是CA对证书请求的回复,只用于导入
p7b以树状展示证书链(certificate chain),同时也支持单个证书,不含私钥。

一 用openssl创建CA证书的RSA密钥(PEM格式):
openssl genrsa -des3 -out ca.key 1024

二用openssl创建CA证书(PEM格式,假如有效期为一年):
openssl req -new -x509 -days 365 -key ca.key -out ca.crt -config openssl.cnf
openssl是可以生成DER格式的CA证书的,最好用IE将PEM格式的CA证书转换成DER格式的CA证书。

三 x509到pfx
pkcs12 -export –in keys/client1.crt -inkey keys/client1.key -out keys/client1.pfx

四 PEM格式的ca.key转换为Microsoft可以识别的pvk格式。
pvk -in ca.key -out ca.pvk -nocrypt -topvk
五 PKCS#12 到 PEM 的转换
openssl pkcs12 -nocerts -nodes -in cert.p12 -out private.pem
验证 openssl pkcs12 -clcerts -nokeys -in cert.p12 -out cert.pem
六 从 PFX 格式文件中提取私钥格式文件 (.key)
openssl pkcs12 -in mycert.pfx -nocerts -nodes -out mycert.key
七 转换 pem 到到 spc
openssl crl2pkcs7 -nocrl -certfile venus.pem  -outform DER -out venus.spc
用 -outform -inform 指定 DER 还是 PAM 格式。例如:
openssl x509 -in Cert.pem -inform PEM -out cert.der -outform DER
八 PEM 到 PKCS#12 的转换,
openssl pkcs12 -export -in Cert.pem -out Cert.p12 -inkey key.pem

密钥库文件格式【Keystore】

格式     :  JKS
扩展名  : .jks/.ks
描述     : 【Java Keystore】密钥库的Java实现版本,provider为SUN
特点     :  密钥库和私钥用不同的密码进行保护

格式     :  JCEKS
扩展名  :  .jce
描述     : 【JCE Keystore】密钥库的JCE实现版本,provider为SUN JCE
特点     :  相对于JKS安全级别更高,保护Keystore私钥时采用TripleDES

格式     :  PKCS12
扩展名  :  .p12/.pfx
描述     : 【PKCS #12】个人信息交换语法标准
特点     :  1、包含私钥、公钥及其证书
2、密钥库和私钥用相同密码进行保护

格式     :  BKS
扩展名  : .bks
描述     :  Bouncycastle Keystore】密钥库的BC实现版本,provider为BC
特点     :  基于JCE实现

格式     : UBER
扩展名  : .ubr
描述     : 【Bouncycastle UBER Keystore】密钥库的BC更安全实现版本,provider为BC

证书文件格式【Certificate】
格式          :  DER
扩展名       :  .cer/.crt/.rsa

描述          : 【ASN .1 DER】用于存放证书
特点          :  不含私钥、二进制

格式          :  PKCS7
扩展名       : .p7b/.p7r
描述          : 【PKCS #7】加密信息语法标准

特点          : 1、p7b以树状展示证书链,不含私钥
2、p7r为CA对证书请求签名的回复,只能用于导入

格式          :  CMS
扩展名       :  .p7c/.p7m/.p7s
描述          : 【Cryptographic Message Syntax】
特点          : 1、p7c只保存证书
2、p7m:signature with enveloped data
3、p7s:时间戳签名文件

格式          :  PEM
扩展名       : .pem
描述          : 【Printable Encoded Message】
特点          : 1、该编码格式在RFC1421中定义,其实PEM是【Privacy-Enhanced Mail】的简写,但他也同样广泛运用于密钥管理
2、ASCII文件
3、一般基于base 64编码
4. Apache 用到的CA证书链就是PEM格式,它实际上可保存普通多个X509证书(.cer),  将每个证书简单加在一起就可以了

格式         :  PKCS10
扩展名      : .p10/.csr
描述         : 【PKCS #10】公钥加密标准【Certificate Signing Request】
特点         :  1、证书签名请求文件
2、ASCII文件
3、CA签名后以p7r文件回复

格式         :  SPC
扩展名      : .pvk/.spc
描述         : 【Software Publishing Certificate】
特点         :  微软公司特有的双证书文件格式,经常用于代码签名,其中
1、pvk用于保存私钥
2、spc用于保存公钥

cer/crt 证书 二进制,不含私钥

pem 跟cer/crt  区别说ASCII 表示

p7b 不含私钥

PKCS

PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准。

What is PKCS? http://www.rsa.com/rsalabs/node.asp?id=2308

PKCS 目前共发布过 15 个标准:

(1)PKCS#1:RSA加密标准。PKCS#1定义了RSA公钥函数的基本格式标准,特别是数字签名。它定义了数字签名如何计算,包括待签名数据和签名本身的格式;它也定义了PSA公/私钥的语法。

(2)PKCS#2:涉及了RSA的消息摘要加密,这已被并入PKCS#1中。

(3)PKCS#3:Diffie-Hellman密钥协议标准。PKCS#3描述了一种实现Diffie- Hellman密钥协议的方法。

(4)PKCS#4:最初是规定RSA密钥语法的,现已经被包含进PKCS#1中。

(5)PKCS#5:基于口令的加密标准。PKCS#5描述了使用由口令生成的密钥来加密8位位组串并产生一个加密的8位位组串的方法。PKCS#5可以用于加密私钥,以便于密钥的安全传输(这在PKCS#8中描述)。

(6)PKCS#6:扩展证书语法标准。PKCS#6定义了提供附加实体信息的X.509证书属性扩展的语法(当PKCS#6第一次发布时,X.509还不支持扩展。这些扩展因此被包括在X.509中)。

(7)PKCS#7:密码消息语法标准。PKCS#7为使用密码算法的数据规定了通用语法,比如数字签名和数字信封。PKCS#7提供了许多格式选项,包括未加密或签名的格式化消息、已封装(加密)消息、已签名消息和既经过签名又经过加密的消息。

(8)PKCS#8:私钥信息语法标准。PKCS#8定义了私钥信息语法和加密私钥语法,其中私钥加密使用了PKCS#5标准。

(9)PKCS#9:可选属性类型。PKCS#9定义了PKCS#6扩展证书、PKCS#7数字签名消息、PKCS#8私钥信息和PKCS#10证书签名请求中要用到的可选属性类型。已定义的证书属性包括E-mail地址、无格式姓名、内容类型、消息摘要、签名时间、签名副本(counter signature)、质询口令字和扩展证书属性。

(10)PKCS#10:证书请求语法标准。PKCS#10定义了证书请求的语法。证书请求包含了一个唯一识别名、公钥和可选的一组属性,它们一起被请求证书的实体签名(证书管理协议中的PKIX证书请求消息就是一个PKCS#10)。

(11)PKCS#11:密码令牌接口标准。PKCS#11或“Cryptoki”为拥有密码信息(如加密密钥和证书)和执行密码学函数的单用户设备定义了一个应用程序接口(API)。智能卡就是实现Cryptoki的典型设备。注意:Cryptoki定义了密码函数接口,但并未指明设备具体如何实现这些函数。而且Cryptoki只说明了密码接口,并未定义对设备来说可能有用的其他接口,如访问设备的文件系统接口。

(12)PKCS#12:个人信息交换语法标准。PKCS#12定义了个人身份信息(包括私钥、证书、各种秘密和扩展字段)的格式。PKCS#12有助于传输证书及对应的私钥,于是用户可以在不同设备间移动他们的个人身份信息。

(13)PDCS#13:椭圆曲线密码标准。PKCS#13标准当前正在完善之中。它包括椭圆曲线参数的生成和验证、密钥生成和验证、数字签名和公钥加密,还有密钥协定,以及参数、密钥和方案标识的ASN.1语法。

(14)PKCS#14:伪随机数产生标准。PKCS#14标准当前正在完善之中。为什么随机数生成也需要建立自己的标准呢?PKI中用到的许多基本的密码学函数,如密钥生成和Diffie-Hellman共享密钥协商,都需要使用随机数。然而,如果“随机数”不是随机的,而是取自一个可预测的取值集合,那么密码学函数就不再是绝对安全了,因为它的取值被限于一个缩小了的值域中。因此,安全伪随机数的生成对于PKI的安全极为关键。

(15)PKCS#15:密码令牌信息语法标准。PKCS#15通过定义令牌上存储的密码对象的通用格式来增进密码令牌的互操作性。在实现PKCS#15的设备上存储的数据对于使用该设备的所有应用程序来说都是一样的,尽管实际上在内部实现时可能所用的格式不同。PKCS#15的实现扮演了翻译家的角色,它在卡的内部格式与应用程序支持的数据格式间进行转换。

X509

X.509是常见通用的证书格式。所有的证书都符合为Public Key Infrastructure (PKI) 制定的 ITU-T X509 国际标准。X.509是国际电信联盟-电信(ITU-T)部分标准和国际标准化组织(ISO)的证书格式标准。作为ITU-ISO目录服务系列标准的一部分,X.509是定义了公钥证书结构的基本标准。1988年首次发布,1993年和1996年两次修订。当前使用的版本是X.509 V3,它加入了扩展字段支持,这极大地增进了证书的灵活性。X.509 V3证书包括一组按预定义顺序排列的强制字段,还有可选扩展字段,即使在强制字段中,X.509证书也允许很大的灵活性,因为它为大多数字段提供了多种编码方案.

PKCS#7 常用的后缀是: .P7B .P7C .SPC
PKCS#12 常用的后缀有: .P12 .PFX
X.509 DER 编码(ASCII)的后缀是: .DER .CER .CRT
X.509 PAM 编码(Base64)的后缀是: .PEM .CER .CRT
.cer/.crt是用于存放证书,它是2进制形式存放的,不含私钥。
.pem跟crt/cer的区别是它以Ascii来表示。
pfx/p12用于存放个人证书/私钥,他通常包含保护密码,2进制方式
p10是证书请求
p7r是CA对证书请求的回复,只用于导入
p7b以树状展示证书链(certificate chain),同时也支持单个证书,不含私钥。

一 用openssl创建CA证书的RSA密钥(PEM格式):
openssl genrsa -des3 -out ca.key 1024

二用openssl创建CA证书(PEM格式,假如有效期为一年):
openssl req -new -x509 -days 365 -key ca.key -out ca.crt -config openssl.cnf
openssl是可以生成DER格式的CA证书的,最好用IE将PEM格式的CA证书转换成DER格式的CA证书。

三 x509到pfx
pkcs12 -export –in keys/client1.crt -inkey keys/client1.key -out keys/client1.pfx

四 PEM格式的ca.key转换为Microsoft可以识别的pvk格式。
pvk -in ca.key -out ca.pvk -nocrypt -topvk
五 PKCS#12 到 PEM 的转换
openssl pkcs12 -nocerts -nodes -in cert.p12 -out private.pem
验证 openssl pkcs12 -clcerts -nokeys -in cert.p12 -out cert.pem
六 从 PFX 格式文件中提取私钥格式文件 (.key)
openssl pkcs12 -in mycert.pfx -nocerts -nodes -out mycert.key
七 转换 pem 到到 spc
openssl crl2pkcs7 -nocrl -certfile venus.pem  -outform DER -out venus.spc
用 -outform -inform 指定 DER 还是 PAM 格式。例如:
openssl x509 -in Cert.pem -inform PEM -out cert.der -outform DER
八 PEM 到 PKCS#12 的转换,
openssl pkcs12 -export -in Cert.pem -out Cert.p12 -inkey key.pem

密钥库文件格式【Keystore】

格式     :  JKS
扩展名  : .jks/.ks
描述     : 【Java Keystore】密钥库的Java实现版本,provider为SUN
特点     :  密钥库和私钥用不同的密码进行保护

格式     :  JCEKS
扩展名  :  .jce
描述     : 【JCE Keystore】密钥库的JCE实现版本,provider为SUN JCE
特点     :  相对于JKS安全级别更高,保护Keystore私钥时采用TripleDES

格式     :  PKCS12
扩展名  :  .p12/.pfx
描述     : 【PKCS #12】个人信息交换语法标准
特点     :  1、包含私钥、公钥及其证书
2、密钥库和私钥用相同密码进行保护

格式     :  BKS
扩展名  : .bks
描述     :  Bouncycastle Keystore】密钥库的BC实现版本,provider为BC
特点     :  基于JCE实现

格式     : UBER
扩展名  : .ubr
描述     : 【Bouncycastle UBER Keystore】密钥库的BC更安全实现版本,provider为BC

证书文件格式【Certificate】
格式          :  DER
扩展名       :  .cer/.crt/.rsa

描述          : 【ASN .1 DER】用于存放证书
特点          :  不含私钥、二进制

格式          :  PKCS7
扩展名       : .p7b/.p7r
描述          : 【PKCS #7】加密信息语法标准

特点          : 1、p7b以树状展示证书链,不含私钥
2、p7r为CA对证书请求签名的回复,只能用于导入

格式          :  CMS
扩展名       :  .p7c/.p7m/.p7s
描述          : 【Cryptographic Message Syntax】
特点          : 1、p7c只保存证书
2、p7m:signature with enveloped data
3、p7s:时间戳签名文件

格式          :  PEM
扩展名       : .pem
描述          : 【Printable Encoded Message】
特点          : 1、该编码格式在RFC1421中定义,其实PEM是【Privacy-Enhanced Mail】的简写,但他也同样广泛运用于密钥管理
2、ASCII文件
3、一般基于base 64编码

格式         :  PKCS10
扩展名      : .p10/.csr
描述         : 【PKCS #10】公钥加密标准【Certificate Signing Request】
特点         :  1、证书签名请求文件
2、ASCII文件
3、CA签名后以p7r文件回复

格式         :  SPC
扩展名      : .pvk/.spc
描述         : 【Software Publishing Certificate】
特点         :  微软公司特有的双证书文件格式,经常用于代码签名,其中
1、pvk用于保存私钥
2、spc用于保存公钥



[iOS]通过openssl库生成pkcs#10证书

OpenSSL:一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。

pkcs#10:PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准。

What is PKCS? http://www.rsa.com/rsalabs/node.asp?id=2308

PKCS 目前共发布过 15 个标准:其中, 
PKCS#10:证书请求语法标准。PKCS#10定义了证书请求的语法。证书请求包含了一个唯一识别名、公钥和可选的一组属性,它们一起被请求证书的实体签名(证书管理协议中的PKIX证书请求消息就是一个PKCS#10)。

Mac osx 系统自带了命令行工具生成p10证书: 
具体参见这里:通过命令行生成p10证书

如果我们想要通过代码方式实现证书请求,就要需要用到openssl的函数。 
1、因为openssl库是基于c语言的库,为方便在Xcode中使用,我们需要把它编译为静态库。具体如何编译,可以参考这篇文件:在iOS中如何编译openssl的静态库

2、如果不想这么麻烦,大家可以在这里下载已经编译好的.a文件。感谢这些前辈们帮我们省了不少事。libssl.a支持多架构

3、第二步下载下来的文件目录结构是这样的: 
a)include目录下包含一个openssl目录,openssl目录底下是所有头文件 
b)libssl.a和libcrypto.a 
4、把上面所有文件拖到工程中,注意:在TARGET->您的项目名称->Build Settings 里面搜索:Header Search Path,在里面增加配置:$(PROJECT_DIR)/include/ 
5、新建一个C File,命名为:header,Xcode会同时帮我们生成一个header.h的头文件。 
在header.h中,我们引用一下头文件,并且声明需要用到的函数.

#include <openssl/x509.h>#include <openssl/rsa.h>#include <openssl/pem.h>//**在这里需要声明函数**/X509_NAME *parse_name(char *subject, long chtype, int multirdn);X509_NAME *CreateDN(char *pbEmail, char *pbCN, char *pbOU, char *pbO, char *pbL, char *pbST, char *pbC);int GenCSR(char *pbDN, int nDNLen, char *pCSR, size_t nCSRSize);

6、在header.c中,实现刚才声明的三个函数。这里只列出GenCSR方法。

int GenCSR(char *pbDN, int nDNLen, char *pCSR, size_t nCSRSize){    char szAltName[] = "DNS:www.jinhill.com";    char szComment[] = "Create by Jinhill";    char szKeyUsage[] = "digitalSignature, nonRepudiation";    char szExKeyUsage[] = "serverAuth, clientAuth";    X509_REQ        *pX509Req = NULL;    int             iRV = 0;    long            lVer = 3;    X509_NAME       *pX509DN = NULL;    EVP_PKEY        *pEVPKey = NULL;    RSA             *pRSA = NULL;    X509_NAME_ENTRY *pX509Entry = NULL;    char            szBuf[255] = {0};    char            mdout[20];    int             nLen, nModLen;    int             bits = 2048;    unsignedlong   E = RSA_3;    unsignedchar   *pDer = NULL;    unsignedchar   *p = NULL;    FILE            *fp = NULL;    const EVP_MD    *md = NULL;    X509            *pX509 = NULL;    BIO             *pBIO = NULL;    BIO             *pPemBIO = NULL;    BUF_MEM         *pBMem = NULL;    //STACK_OF(X509_EXTENSION) *pX509Ext;if(pbDN == NULL)    {        return -1;    }    pX509DN = parse_name(pbDN, V_ASN1_UTF8STRING, 0);    pX509Req = X509_REQ_new();    iRV = X509_REQ_set_version(pX509Req, lVer);    // subject pX509Name    iRV = X509_REQ_set_subject_name(pX509Req, pX509DN);    /* pub key */    pEVPKey = EVP_PKEY_new();    pRSA = RSA_generate_key(bits, E, NULL, NULL);    EVP_PKEY_assign_RSA(pEVPKey, pRSA);    iRV = X509_REQ_set_pubkey(pX509Req, pEVPKey);    /* attribute */    strcpy(szBuf, szAltName);    nLen = strlen(szBuf);    iRV = X509_REQ_add1_attr_by_txt(pX509Req, "subjectAltName", V_ASN1_UTF8STRING, szBuf, nLen);    strcpy(szBuf, szKeyUsage);    nLen = strlen(szBuf);    iRV = X509_REQ_add1_attr_by_txt(pX509Req, "keyUsage", V_ASN1_UTF8STRING, szBuf, nLen);    strcpy(szBuf, szExKeyUsage);    nLen = strlen(szBuf);    iRV = X509_REQ_add1_attr_by_txt(pX509Req, "extendedKeyUsage", V_ASN1_UTF8STRING, szBuf, nLen);    strcpy(szBuf, szComment);    nLen = strlen(szBuf);    iRV = X509_REQ_add1_attr_by_txt(pX509Req, "nsComment", V_ASN1_UTF8STRING, szBuf, nLen);    md = EVP_sha1();    iRV = X509_REQ_digest(pX509Req, md, mdout, &nModLen);    iRV = X509_REQ_sign(pX509Req, pEVPKey, md);    if(!iRV)    {        printf("sign err!\n");        X509_REQ_free(pX509Req);        return -1;    }    // 写入文件PEM格式//  pBIO = BIO_new_file("certreq.txt", "w");//  PEM_write_bio_X509_REQ(pBIO, pX509Req, NULL, NULL);//  BIO_free(pBIO);//返回PEM字符    pPemBIO = BIO_new(BIO_s_mem());//    PEM_write_bio_X509_REQ(pPemBIO, pX509Req, NULL, NULL);    PEM_write_bio_X509_REQ(pPemBIO, pX509Req);    BIO_get_mem_ptr(pPemBIO,&pBMem);    if(pBMem->length <= nCSRSize)    {        memcpy(pCSR, pBMem->data, pBMem->length);    }    BIO_free(pPemBIO);    /* DER编码 *///nLen = i2d_X509_REQ(pX509Req, NULL);//pDer = (unsigned char *)malloc(nLen);//p = pDer;//nLen = i2d_X509_REQ(pX509Req, &p);//free(pDer);//  验证CSR    OpenSSL_add_all_algorithms();    iRV = X509_REQ_verify(pX509Req, pEVPKey);    if(iRV<0)    {        printf("verify err.\n");    }    X509_REQ_free(pX509Req);    return nCSRSize;}

7、测试一下:

- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.char chDN[255] = "/CN=www.jinhill.com/O=Beijing Jinhill Inc./C=CN";    char chCSR[2048] = {0};    int rv = GenCSR(chDN, strlen(chDN), chCSR, sizeof(chCSR));    printf("CSR:\n%s", chCSR);}

可以看到我们生成的证书请求字符串已经打印出来了: 
CSR: 
—–BEGIN CERTIFICATE REQUEST—– 
MIIDFjCCAf4CAQMwRjEYMBYGA1UEAwwPd3d3LmppbmhpbGwuY29tMR0wGwYDVQQK 
DBRCZWlqaW5nIEppbmhpbGwgSW5jLjELMAkGA1UEBgwCQ04wggEgMA0GCSqGSIb3 
DQEBAQUAA4IBDQAwggEIAoIBAQDjf4sXLTio7Qzo9y3eFQsNSzmj/Dxu+DQX4wt3 
Nxk2+ExEP5cq/qPW/GuFJbvBwuxOst3J7wraGgbfSqG5s+cLdy6r4S1Y9HM3Nr23 
MsiKqpVwTvG03HhZk0lAvkeM/0eb2XjijRa3f8Ndrvh56uIldDiWMgGPnFAlv474 
qonEk0tLrBjvKpS+jBB4T03nBOLCdoAO5UNsSTMnt/4ZaakLu+cgxsaV/bh8UxNS 
ff8zP9pGBuHAGl6jA+D/zwK/u8twKI+5zYbdMprBfIEk5f7hYpmFIi/IB85dAkUA 
XVv2JhQ00Yup5/FSNWM3y4f/f4bE7dHEEWolxA0dT222ggZBAgEDoIGMMBwGA1Ud 
ETEVDBNETlM6d3d3LmppbmhpbGwuY29tMB8GA1UdJTEYDBZzZXJ2ZXJBdXRoLCBj 
bGllbnRBdXRoMCAGCWCGSAGG+EIBDTETDBFDcmVhdGUgYnkgSmluaGlsbDApBgNV 
HQ8xIgwgZGlnaXRhbFNpZ25hdHVyZSwgbm9uUmVwdWRpYXRpb24wDQYJKoZIhvcN 
AQEFBQADggEBAEYdkInQcbHdb+jLbEcpM3wBYlObckAMhpzldcg74TXzsm9RfKqO 
hAzmw0MbVAEelCEX42h94AQ9AYFe6WRYUUvmcgPnaQqOHfOaE3cZK0tjB/H2Whj9 
mDz+HA+xoQgRpPbkF19PleJSMp4jL2/a284AKHqE+CBSx+WAiKJmtxnunHejZn7V 
pqbc0eypXKFxOvZKK3WEcVuWmSpacydvSuoKH2cprBJP1fZaXOEPwMgidEEiG60F 
nhbreYnTKXriEzGY0KvN7PtHwXLVr0wKWL6SYpY8xm3pMWSmUUiQz35unnw1VGqF 
6Mt9g0Ir9Dd/IUFjW8XC8L1dvyYIwhVKF50= 
—–END CERTIFICATE REQUEST—–



原创粉丝点击