• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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