Skip to content

Commit

Permalink
Support SM2 certificate signing
Browse files Browse the repository at this point in the history
SM2 certificate signing request can be created and signed by OpenSSL
now, both in library and apps.

Documentation and test cases are added.

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from #9085)
  • Loading branch information
InfoHunter committed Jun 28, 2019
1 parent 53a11c6 commit bc42bd6
Show file tree
Hide file tree
Showing 24 changed files with 501 additions and 44 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -9,6 +9,9 @@

Changes between 1.1.1 and 3.0.0 [xx XXX xxxx]

*) Support SM2 signing and verification schemes with X509 certificate.
[Paul Yang]

*) Use SHA256 as the default digest for TS query in the ts app.
[Tomas Mraz]

Expand Down
68 changes: 63 additions & 5 deletions apps/ca.c
Expand Up @@ -96,7 +96,8 @@ static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509,
const char *enddate,
long days, int batch, const char *ext_sect, CONF *conf,
int verbose, unsigned long certopt, unsigned long nameopt,
int default_op, int ext_copy, int selfsign);
int default_op, int ext_copy, int selfsign,
unsigned char *sm2_id, size_t sm2idlen);
static int certify_cert(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509,
const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts,
STACK_OF(CONF_VALUE) *policy, CA_DB *db,
Expand Down Expand Up @@ -147,7 +148,7 @@ typedef enum OPTION_choice {
OPT_INFILES, OPT_SS_CERT, OPT_SPKAC, OPT_REVOKE, OPT_VALID,
OPT_EXTENSIONS, OPT_EXTFILE, OPT_STATUS, OPT_UPDATEDB, OPT_CRLEXTS,
OPT_RAND_SERIAL,
OPT_R_ENUM,
OPT_R_ENUM, OPT_SM2ID, OPT_SM2HEXID,
/* Do not change the order here; see related case statements below */
OPT_CRL_REASON, OPT_CRL_HOLD, OPT_CRL_COMPROMISE, OPT_CRL_CA_COMPROMISE
} OPTION_CHOICE;
Expand Down Expand Up @@ -217,6 +218,12 @@ const OPTIONS ca_options[] = {
OPT_R_OPTIONS,
#ifndef OPENSSL_NO_ENGINE
{"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
#endif
#ifndef OPENSSL_NO_SM2
{"sm2-id", OPT_SM2ID, 's',
"Specify an ID string to verify an SM2 certificate request"},
{"sm2-hex-id", OPT_SM2HEXID, 's',
"Specify a hex ID string to verify an SM2 certificate request"},
#endif
{NULL}
};
Expand Down Expand Up @@ -262,6 +269,9 @@ int ca_main(int argc, char **argv)
REVINFO_TYPE rev_type = REV_NONE;
X509_REVOKED *r = NULL;
OPTION_CHOICE o;
unsigned char *sm2_id = NULL;
size_t sm2_idlen = 0;
int sm2_free = 0;

prog = opt_init(argc, argv, ca_options);
while ((o = opt_next()) != OPT_EOF) {
Expand Down Expand Up @@ -425,6 +435,30 @@ int ca_main(int argc, char **argv)
case OPT_ENGINE:
e = setup_engine(opt_arg(), 0);
break;
case OPT_SM2ID:
/* we assume the input is not a hex string */
if (sm2_id != NULL) {
BIO_printf(bio_err,
"Use one of the options 'sm2-hex-id' or 'sm2-id'\n");
goto end;
}
sm2_id = (unsigned char *)opt_arg();
sm2_idlen = strlen((const char *)sm2_id);
break;
case OPT_SM2HEXID:
/* try to parse the input as hex string first */
if (sm2_id != NULL) {
BIO_printf(bio_err,
"Use one of the options 'sm2-hex-id' or 'sm2-id'\n");
goto end;
}
sm2_free = 1;
sm2_id = OPENSSL_hexstr2buf(opt_arg(), (long *)&sm2_idlen);
if (sm2_id == NULL) {
BIO_printf(bio_err, "Invalid hex string input\n");
goto end;
}
break;
}
}
end_of_options:
Expand Down Expand Up @@ -913,7 +947,8 @@ int ca_main(int argc, char **argv)
j = certify(&x, infile, pkey, x509p, dgst, sigopts, attribs, db,
serial, subj, chtype, multirdn, email_dn, startdate,
enddate, days, batch, extensions, conf, verbose,
certopt, get_nameopt(), default_op, ext_copy, selfsign);
certopt, get_nameopt(), default_op, ext_copy, selfsign,
sm2_id, sm2_idlen);
if (j < 0)
goto end;
if (j > 0) {
Expand All @@ -932,7 +967,8 @@ int ca_main(int argc, char **argv)
j = certify(&x, argv[i], pkey, x509p, dgst, sigopts, attribs, db,
serial, subj, chtype, multirdn, email_dn, startdate,
enddate, days, batch, extensions, conf, verbose,
certopt, get_nameopt(), default_op, ext_copy, selfsign);
certopt, get_nameopt(), default_op, ext_copy, selfsign,
sm2_id, sm2_idlen);
if (j < 0)
goto end;
if (j > 0) {
Expand Down Expand Up @@ -1230,6 +1266,8 @@ int ca_main(int argc, char **argv)
ret = 0;

end:
if (sm2_free)
OPENSSL_free(sm2_id);
if (ret)
ERR_print_errors(bio_err);
BIO_free_all(Sout);
Expand Down Expand Up @@ -1268,7 +1306,8 @@ static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509,
const char *enddate,
long days, int batch, const char *ext_sect, CONF *lconf,
int verbose, unsigned long certopt, unsigned long nameopt,
int default_op, int ext_copy, int selfsign)
int default_op, int ext_copy, int selfsign,
unsigned char *sm2id, size_t sm2idlen)
{
X509_REQ *req = NULL;
BIO *in = NULL;
Expand Down Expand Up @@ -1300,6 +1339,25 @@ static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509,
BIO_printf(bio_err, "error unpacking public key\n");
goto end;
}
if (sm2id != NULL) {
#ifndef OPENSSL_NO_SM2
ASN1_OCTET_STRING *v;

v = ASN1_OCTET_STRING_new();
if (v == NULL) {
BIO_printf(bio_err, "error: SM2 ID allocation failed\n");
goto end;
}

if (!ASN1_OCTET_STRING_set(v, sm2id, sm2idlen)) {
BIO_printf(bio_err, "error: setting SM2 ID failed\n");
ASN1_OCTET_STRING_free(v);
goto end;
}

X509_REQ_set0_sm2_id(req, v);
#endif
}
i = X509_REQ_verify(req, pktmp);
pktmp = NULL;
if (i < 0) {
Expand Down
149 changes: 143 additions & 6 deletions apps/req.c
Expand Up @@ -90,7 +90,7 @@ typedef enum OPTION_choice {
OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8,
OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509,
OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_ADDEXT, OPT_EXTENSIONS,
OPT_REQEXTS, OPT_PRECERT, OPT_MD,
OPT_REQEXTS, OPT_PRECERT, OPT_MD, OPT_SM2ID, OPT_SM2HEXID,
OPT_R_ENUM
} OPTION_CHOICE;

Expand Down Expand Up @@ -145,6 +145,12 @@ const OPTIONS req_options[] = {
{"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
{"keygen_engine", OPT_KEYGEN_ENGINE, 's',
"Specify engine to be used for key generation operations"},
#endif
#ifndef OPENSSL_NO_SM2
{"sm2-id", OPT_SM2ID, 's',
"Specify an ID string to verify an SM2 certificate request"},
{"sm2-hex-id", OPT_SM2HEXID, 's',
"Specify a hex ID string to verify an SM2 certificate request"},
#endif
{NULL}
};
Expand Down Expand Up @@ -239,6 +245,9 @@ int req_main(int argc, char **argv)
int nodes = 0, newhdr = 0, subject = 0, pubkey = 0, precert = 0;
long newkey = -1;
unsigned long chtype = MBSTRING_ASC, reqflag = 0;
unsigned char *sm2_id = NULL;
size_t sm2_idlen = 0;
int sm2_free = 0;

#ifndef OPENSSL_NO_DES
cipher = EVP_des_ede3_cbc();
Expand Down Expand Up @@ -414,6 +423,29 @@ int req_main(int argc, char **argv)
goto opthelp;
digest = md_alg;
break;
case OPT_SM2ID:
if (sm2_id != NULL) {
BIO_printf(bio_err,
"Use one of the options 'sm2-hex-id' or 'sm2-id'\n");
goto end;
}
sm2_id = (unsigned char *)opt_arg();
sm2_idlen = strlen((const char *)sm2_id);
break;
case OPT_SM2HEXID:
if (sm2_id != NULL) {
BIO_printf(bio_err,
"Use one of the options 'sm2-hex-id' or 'sm2-id'\n");
goto end;
}
/* try to parse the input as hex string first */
sm2_free = 1;
sm2_id = OPENSSL_hexstr2buf(opt_arg(), (long *)&sm2_idlen);
if (sm2_id == NULL) {
BIO_printf(bio_err, "Invalid hex string input\n");
goto end;
}
break;
}
}
argc = opt_num_rest();
Expand Down Expand Up @@ -844,6 +876,26 @@ int req_main(int argc, char **argv)
goto end;
}

if (sm2_id != NULL) {
#ifndef OPENSSL_NO_SM2
ASN1_OCTET_STRING *v;

v = ASN1_OCTET_STRING_new();
if (v == NULL) {
BIO_printf(bio_err, "error: SM2 ID allocation failed\n");
goto end;
}

if (!ASN1_OCTET_STRING_set(v, sm2_id, sm2_idlen)) {
BIO_printf(bio_err, "error: setting SM2 ID failed\n");
ASN1_OCTET_STRING_free(v);
goto end;
}

X509_REQ_set0_sm2_id(req, v);
#endif
}

i = X509_REQ_verify(req, tpubkey);

if (i < 0) {
Expand Down Expand Up @@ -942,6 +994,8 @@ int req_main(int argc, char **argv)
}
ret = 0;
end:
if (sm2_free)
OPENSSL_free(sm2_id);
if (ret) {
ERR_print_errors(bio_err);
}
Expand Down Expand Up @@ -1596,14 +1650,58 @@ static int genpkey_cb(EVP_PKEY_CTX *ctx)
return 1;
}

#ifndef OPENSSL_NO_SM2
static int ec_pkey_is_sm2(EVP_PKEY *pkey)
{
EC_KEY *eckey = NULL;
const EC_GROUP *group = NULL;

if (EVP_PKEY_id(pkey) == EVP_PKEY_SM2)
return 1;
if (EVP_PKEY_id(pkey) == EVP_PKEY_EC
&& (eckey = EVP_PKEY_get0_EC_KEY(pkey)) != NULL
&& (group = EC_KEY_get0_group(eckey)) != NULL
&& EC_GROUP_get_curve_name(group) == NID_sm2)
return 1;
return 0;
}
#endif

static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey,
const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts)
{
EVP_PKEY_CTX *pkctx = NULL;
int i, def_nid;
#ifndef OPENSSL_NO_SM2
EVP_PKEY_CTX *pctx = NULL;
#endif
int i, def_nid, ret = 0;

if (ctx == NULL)
return 0;
goto err;
#ifndef OPENSSL_NO_SM2
if (ec_pkey_is_sm2(pkey)) {
/* initialize some SM2-specific code */
if (!EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2)) {
BIO_printf(bio_err, "Internal error.\n");
goto err;
}
pctx = EVP_PKEY_CTX_new(pkey, NULL);
if (pctx == NULL) {
BIO_printf(bio_err, "memory allocation failure.\n");
goto err;
}
/* set SM2 ID from sig options before calling the real init routine */
for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) {
char *sigopt = sk_OPENSSL_STRING_value(sigopts, i);
if (pkey_ctrl_string(pctx, sigopt) <= 0) {
BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt);
ERR_print_errors(bio_err);
goto err;
}
}
EVP_MD_CTX_set_pkey_ctx(ctx, pctx);
}
#endif
/*
* EVP_PKEY_get_default_digest_nid() returns 2 if the digest is mandatory
* for this algorithm.
Expand All @@ -1614,27 +1712,44 @@ static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey,
md = NULL;
}
if (!EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey))
return 0;
goto err;
for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) {
char *sigopt = sk_OPENSSL_STRING_value(sigopts, i);
if (pkey_ctrl_string(pkctx, sigopt) <= 0) {
BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt);
ERR_print_errors(bio_err);
return 0;
goto err;
}
}
return 1;

ret = 1;
err:
#ifndef OPENSSL_NO_SM2
if (!ret)
EVP_PKEY_CTX_free(pctx);
#endif
return ret;
}

int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md,
STACK_OF(OPENSSL_STRING) *sigopts)
{
int rv;
EVP_MD_CTX *mctx = EVP_MD_CTX_new();
#ifndef OPENSSL_NO_SM2
EVP_PKEY_CTX *pctx = NULL;
#endif

rv = do_sign_init(mctx, pkey, md, sigopts);
if (rv > 0)
rv = X509_sign_ctx(x, mctx);
#ifndef OPENSSL_NO_SM2
/* only in SM2 case we need to free the pctx explicitly */
if (ec_pkey_is_sm2(pkey)) {
pctx = EVP_MD_CTX_pkey_ctx(mctx);
EVP_PKEY_CTX_free(pctx);
}
#endif
EVP_MD_CTX_free(mctx);
return rv > 0 ? 1 : 0;
}
Expand All @@ -1644,9 +1759,20 @@ int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md,
{
int rv;
EVP_MD_CTX *mctx = EVP_MD_CTX_new();
#ifndef OPENSSL_NO_SM2
EVP_PKEY_CTX *pctx = NULL;
#endif

rv = do_sign_init(mctx, pkey, md, sigopts);
if (rv > 0)
rv = X509_REQ_sign_ctx(x, mctx);
#ifndef OPENSSL_NO_SM2
/* only in SM2 case we need to free the pctx explicitly */
if (ec_pkey_is_sm2(pkey)) {
pctx = EVP_MD_CTX_pkey_ctx(mctx);
EVP_PKEY_CTX_free(pctx);
}
#endif
EVP_MD_CTX_free(mctx);
return rv > 0 ? 1 : 0;
}
Expand All @@ -1656,9 +1782,20 @@ int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md,
{
int rv;
EVP_MD_CTX *mctx = EVP_MD_CTX_new();
#ifndef OPENSSL_NO_SM2
EVP_PKEY_CTX *pctx = NULL;
#endif

rv = do_sign_init(mctx, pkey, md, sigopts);
if (rv > 0)
rv = X509_CRL_sign_ctx(x, mctx);
#ifndef OPENSSL_NO_SM2
/* only in SM2 case we need to free the pctx explicitly */
if (ec_pkey_is_sm2(pkey)) {
pctx = EVP_MD_CTX_pkey_ctx(mctx);
EVP_PKEY_CTX_free(pctx);
}
#endif
EVP_MD_CTX_free(mctx);
return rv > 0 ? 1 : 0;
}

0 comments on commit bc42bd6

Please sign in to comment.