1 /*
2 * Copyright 2014-2022 The GmSSL Project. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the License); you may
5 * not use this file except in compliance with the License.
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 */
9
10
11 #include <stdio.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <sys/stat.h>
16 #include <gmssl/cms.h>
17 #include <gmssl/x509.h>
18 #include <gmssl/rand.h>
19
20
21 /*
22
23 签名的时候要提供签名者的证书,并且提供签名私钥
24 但是验证的时候假定CMS中已经包含签名者的证书了,但是我们要提供CA证书库
25
26 加密的时候要指定接收者的证书,并且可以有多个接收者
27 解密的时候只提供一个解密私钥,但是最好配合解密者的证书,从这个证书中找到解密者的名字
28
29 如果即加密又签名,那么输出的是SignedAndEnveloped
30
31 CMS有PEM吗?
32
33 cms -encrypt -rcpt a.pem -rcpt b.pem -rcpt c.pem -in file -sign -signcert a.pem -signcert b.pem
34 -rcptcert -rcpt_cert -sign_cert b.pem -signkey
35
36 首先接收者可以有多个证书
37
38 这里面有个问题,因为我们要输出一个加密的对象,因此我们必须把输入的内容读取进来。
39
40
41 EnvelopedData 是一个封装的SEQUENCE中,因此必须读取所有的内容。
42 如果是一个文件,就需要读取所有的文件内容,如果是一个stream ,也需要读取完整的内容到一个足够大的buffer中,如何设置这个buffer的大小呢
43
44
45
46 对于输入文件,如果输入有文件名的话,可以直接通过stat获取文件长度
47 但是如果对于stream的话,实际上我们是没有办法获得输入长度的,那么就直接准备一个buffer好了。
48 不要给自己找麻烦了,直接只支持文件输入吧
49 encrypt
50
51 */
52
53 static const char *options = "-encrypt (-rcptcert pem)* -in file -out file";
54
55
get_files_size(int argc,char ** argv,const char * option,size_t * len)56 static int get_files_size(int argc, char **argv, const char *option, size_t *len)
57 {
58 char *prog = argv[0];
59 char *file = NULL;
60 FILE *fp = NULL;
61 struct stat st;
62
63 argc--;
64 argv++;
65
66 *len = 0;
67 while (argc > 1) {
68 if (!strcmp(*argv, option)) {
69 if (--argc < 1) {
70 fprintf(stderr, "%s: '%s' option value missing\n", prog, *argv);
71 return -1;
72 }
73 file = *(++argv);
74 if (!(fp = fopen(file, "r"))) {
75 fprintf(stderr, "%s: open '%s' failed : %s\n", prog, file, strerror(errno));
76 return -1;
77 }
78 if (fstat(fileno(fp), &st) < 0) {
79 fprintf(stderr, "%s: access '%s' failed : %s\n", prog, file, strerror(errno));
80 fclose(fp);
81 return -1;
82 }
83 *len += st.st_size;
84 fclose(fp);
85 }
86 argc--;
87 argv++;
88 }
89
90 return 1;
91 }
92
cmsencrypt_main(int argc,char ** argv)93 int cmsencrypt_main(int argc, char **argv)
94 {
95 int ret = 1;
96 char *prog = argv[0];
97 int op = 0;
98 char *infile = NULL;
99 char *outfile = NULL;
100 FILE *infp = stdin;
101 FILE *outfp = stdout;
102 uint8_t *rcpt_certs = NULL;
103 size_t rcpt_certs_len;
104 uint8_t key[16];
105 uint8_t iv[16];
106 uint8_t *inbuf = NULL;
107 size_t inlen;
108 uint8_t *cms = NULL;
109 size_t cmslen;
110 uint8_t *cert;
111
112 if (argc < 2) {
113 fprintf(stderr, "usage: %s %s\n", prog, options);
114 return 1;
115 }
116
117 // 预先统计证书缓冲大小和输入大小
118 if (get_files_size(argc, argv, "-rcptcert", &rcpt_certs_len) != 1) {
119 goto end;
120 }
121 if (rcpt_certs_len <= 0) {
122 fprintf(stderr, "%s: invalid cert length\n", prog);
123 goto end;
124 }
125 rcpt_certs_len = (rcpt_certs_len * 3)/4;
126 if (!(rcpt_certs = malloc(rcpt_certs_len))) {
127 fprintf(stderr, "%s: malloc failure\n", prog);
128 goto end;
129 }
130 cert = rcpt_certs;
131
132 if (get_files_size(argc, argv, "-in", &inlen) != 1) {
133 goto end;
134 }
135 if (inlen <= 0) {
136 fprintf(stderr, "%s: invalid input length\n", prog);
137 goto end;
138 }
139 if (!(inbuf = malloc(inlen))) {
140 fprintf(stderr, "%s: %s\n", prog, strerror(errno));
141 goto end;
142 }
143
144 argc--;
145 argv++;
146
147 while (argc > 1) {
148 if (!strcmp(*argv, "-help")) {
149 printf("usage: %s %s\n", prog, options);
150 ret = 0;
151 goto end;
152 } else if (!strcmp(*argv, "-rcptcert")) {
153 char *certfile;
154 FILE *certfp;
155 size_t certlen;
156 if (--argc < 1) goto bad;
157 certfile = *(++argv);
158 if (!(certfp = fopen(certfile, "r"))) {
159 fprintf(stderr, "%s: open '%s' failure : %s\n", prog, certfile, strerror(errno));
160 goto end;
161 }
162 if (x509_cert_from_pem(cert, &certlen, rcpt_certs_len, certfp) != 1) {
163 fprintf(stderr, "%s: error\n", prog);
164 fclose(certfp);
165 goto end;
166 }
167 cert += certlen;
168 fclose(certfp);
169 } else if (!strcmp(*argv, "-in")) {
170 if (--argc < 1) goto bad;
171 infile = *(++argv);
172 if (!(infp = fopen(infile, "r"))) {
173 fprintf(stderr, "%s: open '%s' failure : %s\n", prog, infile, strerror(errno));
174 goto end;
175 }
176 if ((inlen = fread(inbuf, 1, inlen, infp)) <= 0) {
177 fprintf(stderr, "%s: read data error: %s\n", prog, strerror(errno));
178 goto end;
179 }
180 } else if (!strcmp(*argv, "-out")) {
181 if (--argc < 1) goto bad;
182 outfile = *(++argv);
183 if (!(outfp = fopen(outfile, "w"))) {
184 fprintf(stderr, "%s: open '%s' failure : %s\n", prog, outfile, strerror(errno));
185 goto end;
186 }
187 } else {
188 fprintf(stderr, "%s: illegal option '%s'\n", prog, *argv);
189 goto end;
190 bad:
191 fprintf(stderr, "%s: '%s' option value missing\n", prog, *argv);
192 goto end;
193 }
194
195 argc--;
196 argv++;
197 }
198
199 rcpt_certs_len = cert - rcpt_certs;
200
201 if (rand_bytes(key, sizeof(key)) != 1
202 || rand_bytes(iv, sizeof(iv)) != 1
203 || cms_envelop(NULL, &cmslen, rcpt_certs, rcpt_certs_len,
204 OID_sm4_cbc, key, sizeof(key), iv, sizeof(iv),
205 OID_cms_data, inbuf, inlen, NULL, 0, NULL, 0) != 1) {
206 fprintf(stderr, "%s: inner error\n", prog);
207 goto end;
208 }
209 if (!(cms = malloc(cmslen))) {
210 fprintf(stderr, "%s: malloc failure\n", prog);
211 goto end;
212 }
213 if (cms_envelop(cms, &cmslen, rcpt_certs, rcpt_certs_len,
214 OID_sm4_cbc, key, sizeof(key), iv, sizeof(iv),
215 OID_cms_data, inbuf, inlen, NULL, 0, NULL, 0) != 1) {
216 fprintf(stderr, "%s: inner error\n", prog);
217 goto end;
218 }
219 if (cms_to_pem(cms, cmslen, outfp) != 1) {
220 fprintf(stderr, "%s: output CMS failure\n", prog);
221 goto end;
222 }
223
224 ret = 0;
225
226 end:
227 if (infile && infp) fclose(infp);
228 if (outfile && outfp) fclose(outfp);
229 if (rcpt_certs) free(rcpt_certs);
230 if (cms) free(cms);
231 return ret;
232 }
233