* This file is part of the openHiTLS project.
*
* openHiTLS is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "app_x509.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stddef.h>
#include <limits.h>
#include "bsl_list.h"
#include "bsl_print.h"
#include "bsl_conf_def.h"
#include "crypt_errno.h"
#include "crypt_eal_rand.h"
#include "crypt_codecskey.h"
#include "crypt_eal_codecs.h"
#include "crypt_eal_md.h"
#include "hitls_pki_errno.h"
#include "app_errno.h"
#include "app_print.h"
#include "app_conf.h"
#include "app_opt.h"
#include "app_utils.h"
#include "app_list.h"
#define X509_DEFAULT_CERT_DAYS 30
#define X509_DEFAULT_SERIAL_SIZE 20
#define X509_DAY_SECONDS (24 * 60 * 60)
#define X509_SET_SERIAL_PREFIX "0x"
#define X509_MAX_MD_LEN 64
typedef enum {
HITLS_APP_OPT_IN = 2,
HITLS_APP_OPT_INFORM,
HITLS_APP_OPT_REQ,
HITLS_APP_OPT_OUT,
HITLS_APP_OPT_OUTFORM,
HITLS_APP_OPT_NOOUT,
HITLS_APP_OPT_TEXT,
HITLS_APP_OPT_ISSUER,
HITLS_APP_OPT_SUBJECT,
HITLS_APP_OPT_NAMEOPT,
HITLS_APP_OPT_SUBJECT_HASH,
HITLS_APP_OPT_FINGERPRINT,
HITLS_APP_OPT_PUBKEY,
HITLS_APP_OPT_DAYS,
HITLS_APP_OPT_SET_SERIAL,
HITLS_APP_OPT_EXT_FILE,
HITLS_APP_OPT_EXT_SECTION,
HITLS_APP_OPT_MD_ALG,
HITLS_APP_OPT_SIGN_KEY,
HITLS_APP_OPT_PASSIN,
HITLS_APP_OPT_CA,
HITLS_APP_OPT_CA_KEY,
HITLS_APP_OPT_USERID,
HITLS_APP_OPT_COPY_EXTENSIONS,
} HITLSOptType;
static const HITLS_CmdOption g_x509Opts[] = {
{"help", HITLS_APP_OPT_HELP, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Display this function summary"},
{"in", HITLS_APP_OPT_IN, HITLS_APP_OPT_VALUETYPE_IN_FILE, "Input file"},
{"inform", HITLS_APP_OPT_INFORM, HITLS_APP_OPT_VALUETYPE_FMT_PEMDER, "Input format"},
{"req", HITLS_APP_OPT_REQ, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Input is a csr, sign and output"},
{"out", HITLS_APP_OPT_OUT, HITLS_APP_OPT_VALUETYPE_OUT_FILE, "Output file"},
{"outform", HITLS_APP_OPT_OUTFORM, HITLS_APP_OPT_VALUETYPE_FMT_PEMDER, "Output format"},
{"noout", HITLS_APP_OPT_NOOUT, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "No Cert output "},
{"nameopt", HITLS_APP_OPT_NAMEOPT, HITLS_APP_OPT_VALUETYPE_STRING,
"Cert name options: oneline|multiline|rfc2253 - def oneline"},
{"issuer", HITLS_APP_OPT_ISSUER, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print issuer DN"},
{"subject", HITLS_APP_OPT_SUBJECT, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print subject DN"},
{"hash", HITLS_APP_OPT_SUBJECT_HASH, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print subject DN hash"},
{"fingerprint", HITLS_APP_OPT_FINGERPRINT, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print fingerprint"},
{"pubkey", HITLS_APP_OPT_PUBKEY, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Output the pubkey"},
{"text", HITLS_APP_OPT_TEXT, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print x509 cert in text"},
{"days", HITLS_APP_OPT_DAYS, HITLS_APP_OPT_VALUETYPE_POSITIVE_INT,
"How long before the certificate expires - def 30 days"},
{"set_serial", HITLS_APP_OPT_SET_SERIAL, HITLS_APP_OPT_VALUETYPE_STRING, "Cer serial number"},
{"extfile", HITLS_APP_OPT_EXT_FILE, HITLS_APP_OPT_VALUETYPE_IN_FILE, "File with x509v3 extension to add"},
{"extensions", HITLS_APP_OPT_EXT_SECTION, HITLS_APP_OPT_VALUETYPE_STRING, "Section from config file to use"},
{"md", HITLS_APP_OPT_MD_ALG, HITLS_APP_OPT_VALUETYPE_STRING, "Any supported digest algorithm."},
{"signkey", HITLS_APP_OPT_SIGN_KEY, HITLS_APP_OPT_VALUETYPE_IN_FILE,
"Privkey file for self sign cert, must be PEM format"},
{"passin", HITLS_APP_OPT_PASSIN, HITLS_APP_OPT_VALUETYPE_STRING, "Private key and cert file pass-phrase source"},
{"CA", HITLS_APP_OPT_CA, HITLS_APP_OPT_VALUETYPE_IN_FILE, "CA certificate, must be PEM format"},
{"CAkey", HITLS_APP_OPT_CA_KEY, HITLS_APP_OPT_VALUETYPE_IN_FILE, "CA key, must be PEM format"},
{"userid", HITLS_APP_OPT_USERID, HITLS_APP_OPT_VALUETYPE_STRING, "User ID for SM2"},
{"copy_extensions", HITLS_APP_OPT_COPY_EXTENSIONS, HITLS_APP_OPT_VALUETYPE_STRING,
"Copy extensions from CSR: none|copyall"},
{NULL, 0, 0, NULL},
};
typedef struct {
bool req;
char *inPath;
BSL_ParseFormat inForm;
char *outPath;
BSL_ParseFormat outForm;
bool noout;
char *passInArg;
} X509GeneralOpts;
typedef struct {
int32_t nameOpt;
bool issuer;
bool subject;
bool subjectHash;
bool text;
int32_t mdId;
bool fingerprint;
bool pubKey;
} X509PrintOpts;
typedef struct {
int32_t mdId;
int64_t days;
uint8_t *serial;
uint32_t serialLen;
char *extFile;
char *extSection;
char *signKeyPath;
char *caPath;
char *caKeyPath;
bool copyCsrExtensions;
} X509CertOpts;
typedef struct {
X509GeneralOpts generalOpts;
X509PrintOpts printOpts;
X509CertOpts certOpts;
BSL_UIO *outUio;
BSL_CONF *conf;
HITLS_X509_Cert *cert;
HITLS_X509_Cert *ca;
HITLS_X509_Csr *csr;
HITLS_X509_Ext *certExt;
CRYPT_EAL_PkeyCtx *privKey;
char *passin;
BSL_Buffer encodeCert;
char *userId;
} X509OptCtx;
typedef int32_t (*X509OptHandleFunc)(X509OptCtx *);
typedef struct {
int optType;
X509OptHandleFunc func;
} X509OptHandleFuncMap;
typedef int32_t (*ExtConfHandleFunc)(char *cnfValue, X509OptCtx *optCtx);
typedef struct {
char *extName;
ExtConfHandleFunc func;
} X509ExtHandleFuncMap;
typedef struct {
const char *nameopt;
int32_t printFlag;
} X509NamePrintFlag;
typedef int32_t (*PrintX509Func)(const X509OptCtx *);
* 6 types of data printing:
* 1. issuer
* 2. subject
* 3. hash
* 4. fingerprint
* 5. pubKey
* 6. cert
*/
PrintX509Func g_printX509FuncList[] = {NULL, NULL, NULL, NULL, NULL, NULL};
#define PRINT_X509_FUNC_LIST_CNT (sizeof(g_printX509FuncList) / sizeof(PrintX509Func))
static void AppPushPrintX509Func(PrintX509Func func)
{
for (size_t i = 0; i < PRINT_X509_FUNC_LIST_CNT; ++i) {
if ((g_printX509FuncList[i] == NULL) || (g_printX509FuncList[i] == func)) {
g_printX509FuncList[i] = func;
return;
}
}
}
static int32_t AppPrintX509(const X509OptCtx *optCtx)
{
int32_t ret = HITLS_APP_SUCCESS;
for (size_t i = 0; i < PRINT_X509_FUNC_LIST_CNT; ++i) {
if ((g_printX509FuncList[i] != NULL)) {
ret = g_printX509FuncList[i](optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
}
}
return ret;
}
static void ResetPrintX509FuncList(void)
{
for (size_t i = 0; i < PRINT_X509_FUNC_LIST_CNT; ++i) {
g_printX509FuncList[i] = NULL;
}
}
static int32_t PrintIssuer(const X509OptCtx *optCtx)
{
BslList *issuer = NULL;
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_GET_ISSUER_DN, &issuer, sizeof(BslList *));
if (ret != 0) {
AppPrintError("x509: Get issuer name failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = BSL_PRINT_Fmt(0, optCtx->outUio,
optCtx->printOpts.nameOpt == HITLS_PKI_PRINT_DN_MULTILINE ? "Issuer=\n" : "Issuer=");
if (ret != 0) {
AppPrintError("x509: Print issuer name failed, errCode=%d.\n", ret);
return HITLS_APP_BSL_FAIL;
}
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_DNNAME, issuer, sizeof(BslList), optCtx->outUio);
if (ret != 0) {
AppPrintError("x509: Print issuer failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintSubject(const X509OptCtx *optCtx)
{
BslList *subject = NULL;
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_GET_SUBJECT_DN, &subject, sizeof(BslList *));
if (ret != 0) {
AppPrintError("x509: Get subject name failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = BSL_PRINT_Fmt(0, optCtx->outUio,
optCtx->printOpts.nameOpt == HITLS_PKI_PRINT_DN_MULTILINE ? "Subject=\n" : "Subject=");
if (ret != 0) {
AppPrintError("x509: Print subject name failed, errCode=%d.\n", ret);
return HITLS_APP_BSL_FAIL;
}
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_DNNAME, subject, sizeof(BslList), optCtx->outUio);
if (ret != 0) {
AppPrintError("x509: Print subject failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintSubjectHash(const X509OptCtx *optCtx)
{
BslList *subject = NULL;
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_GET_SUBJECT_DN, &subject, sizeof(BslList *));
if (ret != 0) {
AppPrintError("x509: Get subject name for hash failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_DNNAME_HASH, subject, sizeof(BslList), optCtx->outUio);
if (ret != 0) {
AppPrintError("x509: Print subject hash failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintFingerPrint(const X509OptCtx *optCtx)
{
uint8_t md[X509_MAX_MD_LEN] = {0};
uint32_t mdLen = X509_MAX_MD_LEN;
if (optCtx->printOpts.mdId == CRYPT_MD_SHAKE128) {
mdLen = HITLS_APP_SHAKE128_SIZE;
} else if (optCtx->printOpts.mdId == CRYPT_MD_SHAKE256) {
mdLen = HITLS_APP_SHAKE256_SIZE;
}
int32_t ret = HITLS_X509_CertDigest(optCtx->cert, optCtx->printOpts.mdId, md, &mdLen);
if (ret != 0) {
AppPrintError("x509: Get cert digest failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = BSL_PRINT_Fmt(0, optCtx->outUio, "%s Fingerprint=",
HITLS_APP_GetNameByCid(optCtx->printOpts.mdId, HITLS_APP_LIST_OPT_DGST_ALG));
if (ret != 0) {
AppPrintError("x509: Print fingerprint failed, errCode=%d.\n", ret);
return HITLS_APP_BSL_FAIL;
}
ret = BSL_PRINT_Hex(0, true, md, mdLen, optCtx->outUio);
if (ret != 0) {
AppPrintError("x509: Print fingerprint failed, errCode=%d.\n", ret);
return HITLS_APP_BSL_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintCert(const X509OptCtx *optCtx)
{
int32_t ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_CERT, optCtx->cert, sizeof(HITLS_X509_Cert *), optCtx->outUio);
if (ret != 0) {
AppPrintError("x509: Print cert failed, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509OptErr(X509OptCtx *optCtx)
{
(void)optCtx;
AppPrintError("x509: Use -help for summary.\n");
return HITLS_APP_OPT_UNKOWN;
}
static int32_t X509OptHelp(X509OptCtx *optCtx)
{
(void)optCtx;
HITLS_APP_OptHelpPrint(g_x509Opts);
return HITLS_APP_HELP;
}
static int32_t X509OptIn(X509OptCtx *optCtx)
{
optCtx->generalOpts.inPath = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptOut(X509OptCtx *optCtx)
{
optCtx->generalOpts.outPath = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptInForm(X509OptCtx *optCtx)
{
char *str = HITLS_APP_OptGetValueStr();
int32_t ret =
HITLS_APP_OptGetFormatType(str, HITLS_APP_OPT_VALUETYPE_FMT_PEMDER, (uint32_t *)&optCtx->generalOpts.inForm);
if (ret != HITLS_APP_SUCCESS) {
AppPrintError("x509: Invalid format \"%s\" for -inform.\nx509: Use -help for summary.\n", str);
}
return ret;
}
static int32_t X509OptOutForm(X509OptCtx *optCtx)
{
char *str = HITLS_APP_OptGetValueStr();
int32_t ret =
HITLS_APP_OptGetFormatType(str, HITLS_APP_OPT_VALUETYPE_FMT_PEMDER, (uint32_t *)&optCtx->generalOpts.outForm);
if (ret != HITLS_APP_SUCCESS) {
AppPrintError("x509: Invalid format \"%s\" for -outform.\nx509: Use -help for summary.\n", str);
}
return ret;
}
static int32_t X509OptReq(X509OptCtx *optCtx)
{
optCtx->generalOpts.req = true;
return HITLS_APP_SUCCESS;
}
static int32_t X509OptNoout(X509OptCtx *optCtx)
{
optCtx->generalOpts.noout = true;
return HITLS_APP_SUCCESS;
}
static int32_t X509OptIssuer(X509OptCtx *optCtx)
{
optCtx->printOpts.issuer = true;
AppPushPrintX509Func(PrintIssuer);
return HITLS_APP_SUCCESS;
}
static int32_t X509OptSubject(X509OptCtx *optCtx)
{
optCtx->printOpts.subject = true;
AppPushPrintX509Func(PrintSubject);
return HITLS_APP_SUCCESS;
}
static int32_t X509OptNameOpt(X509OptCtx *optCtx)
{
static const X509NamePrintFlag printFlags[] = {
{"oneline", HITLS_PKI_PRINT_DN_ONELINE},
{"multiline", HITLS_PKI_PRINT_DN_MULTILINE},
{"rfc2253", HITLS_PKI_PRINT_DN_RFC2253},
};
char *str = HITLS_APP_OptGetValueStr();
for (size_t i = 0; i < (sizeof(printFlags) / sizeof(X509NamePrintFlag)); ++i) {
if (strcmp(printFlags[i].nameopt, str) == 0) {
optCtx->printOpts.nameOpt = printFlags[i].printFlag;
return HITLS_APP_SUCCESS;
}
}
AppPrintError("x509: Invalid nameopt %s.\nx509: Use -help for summary.\n", str);
return HITLS_APP_OPT_VALUE_INVALID;
}
static int32_t X509OptSubjectHash(X509OptCtx *optCtx)
{
(void)optCtx;
AppPushPrintX509Func(PrintSubjectHash);
return HITLS_APP_SUCCESS;
}
static int32_t X509OptFingerprint(X509OptCtx *optCtx)
{
(void)optCtx;
AppPushPrintX509Func(PrintFingerPrint);
return HITLS_APP_SUCCESS;
}
static int32_t X509OptText(X509OptCtx *optCtx)
{
optCtx->printOpts.text = true;
AppPushPrintX509Func(PrintCert);
return HITLS_APP_SUCCESS;
}
static int32_t X509OptPubkey(X509OptCtx *optCtx)
{
(void)optCtx;
optCtx->printOpts.pubKey = true;
return HITLS_APP_SUCCESS;
}
static int32_t X509OptMdId(X509OptCtx *optCtx)
{
optCtx->certOpts.mdId = HITLS_APP_GetCidByName(HITLS_APP_OptGetValueStr(), HITLS_APP_LIST_OPT_DGST_ALG);
optCtx->printOpts.mdId = optCtx->certOpts.mdId;
return optCtx->certOpts.mdId == BSL_CID_UNKNOWN ? HITLS_APP_OPT_VALUE_INVALID : HITLS_APP_SUCCESS;
}
static int32_t X509OptDays(X509OptCtx *optCtx)
{
int32_t ret = HITLS_APP_OptGetUint32(HITLS_APP_OptGetValueStr(), (uint32_t *)&optCtx->certOpts.days);
if (ret != HITLS_APP_SUCCESS) {
AppPrintError("x509: Invalid days.\nx509: Use -help for summary.\n");
}
return ret;
}
static int32_t X509OptSetSerial(X509OptCtx *optCtx)
{
char *str = HITLS_APP_OptGetValueStr();
int32_t ret = HITLS_APP_ParseHex(str, true, &optCtx->certOpts.serial, &optCtx->certOpts.serialLen);
if (ret != HITLS_APP_SUCCESS) {
AppPrintError("x509: Invalid serial: %s.\n", str);
}
return ret;
}
static int32_t X509OptExtFile(X509OptCtx *optCtx)
{
optCtx->certOpts.extFile = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptExtSection(X509OptCtx *optCtx)
{
optCtx->certOpts.extSection = HITLS_APP_OptGetValueStr();
if (strlen(optCtx->certOpts.extSection) > BSL_CONF_SEC_SIZE) {
AppPrintError("x509: Invalid extensions, size should less than %d.\n", BSL_CONF_SEC_SIZE);
return HITLS_APP_OPT_VALUE_INVALID;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509OptSignKey(X509OptCtx *optCtx)
{
optCtx->certOpts.signKeyPath = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptPassin(X509OptCtx *optCtx)
{
optCtx->generalOpts.passInArg = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptCa(X509OptCtx *optCtx)
{
optCtx->certOpts.caPath = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptCaKey(X509OptCtx *optCtx)
{
optCtx->certOpts.caKeyPath = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509UserId(X509OptCtx *optCtx)
{
optCtx->userId = HITLS_APP_OptGetValueStr();
return HITLS_APP_SUCCESS;
}
static int32_t X509OptCopyExtensions(X509OptCtx *optCtx)
{
char *str = HITLS_APP_OptGetValueStr();
if (strcmp(str, "none") == 0) {
optCtx->certOpts.copyCsrExtensions = false;
return HITLS_APP_SUCCESS;
}
if (strcmp(str, "copyall") == 0) {
optCtx->certOpts.copyCsrExtensions = true;
return HITLS_APP_SUCCESS;
}
AppPrintError("x509: Invalid copy_extensions %s. Use none or copyall.\n", str);
return HITLS_APP_OPT_VALUE_INVALID;
}
static const X509OptHandleFuncMap g_x509OptHandleFuncMap[] = {
{HITLS_APP_OPT_ERR, X509OptErr},
{HITLS_APP_OPT_HELP, X509OptHelp},
{HITLS_APP_OPT_IN, X509OptIn},
{HITLS_APP_OPT_INFORM, X509OptInForm},
{HITLS_APP_OPT_REQ, X509OptReq},
{HITLS_APP_OPT_OUT, X509OptOut},
{HITLS_APP_OPT_OUTFORM, X509OptOutForm},
{HITLS_APP_OPT_NOOUT, X509OptNoout},
{HITLS_APP_OPT_ISSUER, X509OptIssuer},
{HITLS_APP_OPT_SUBJECT, X509OptSubject},
{HITLS_APP_OPT_NAMEOPT, X509OptNameOpt},
{HITLS_APP_OPT_SUBJECT_HASH, X509OptSubjectHash},
{HITLS_APP_OPT_FINGERPRINT, X509OptFingerprint},
{HITLS_APP_OPT_PUBKEY, X509OptPubkey},
{HITLS_APP_OPT_TEXT, X509OptText},
{HITLS_APP_OPT_MD_ALG, X509OptMdId},
{HITLS_APP_OPT_DAYS, X509OptDays},
{HITLS_APP_OPT_SET_SERIAL, X509OptSetSerial},
{HITLS_APP_OPT_EXT_FILE, X509OptExtFile},
{HITLS_APP_OPT_EXT_SECTION, X509OptExtSection},
{HITLS_APP_OPT_SIGN_KEY, X509OptSignKey},
{HITLS_APP_OPT_PASSIN, X509OptPassin},
{HITLS_APP_OPT_CA, X509OptCa},
{HITLS_APP_OPT_CA_KEY, X509OptCaKey},
{HITLS_APP_OPT_USERID, X509UserId},
{HITLS_APP_OPT_COPY_EXTENSIONS, X509OptCopyExtensions},
};
static int32_t ParseX509Opt(int argc, char *argv[], X509OptCtx *optCtx)
{
int32_t ret = HITLS_APP_OptBegin(argc, argv, g_x509Opts);
if (ret != HITLS_APP_SUCCESS) {
HITLS_APP_OptEnd();
AppPrintError("error in opt begin.\n");
return ret;
}
int optType = HITLS_APP_OPT_ERR;
while ((ret == HITLS_APP_SUCCESS) && ((optType = HITLS_APP_OptNext()) != HITLS_APP_OPT_EOF)) {
for (size_t i = 0; i < (sizeof(g_x509OptHandleFuncMap) / sizeof(g_x509OptHandleFuncMap[0])); ++i) {
if (optType == g_x509OptHandleFuncMap[i].optType) {
ret = g_x509OptHandleFuncMap[i].func(optCtx);
break;
}
}
}
if ((ret == HITLS_APP_SUCCESS) && (HITLS_APP_GetRestOptNum() != 0)) {
AppPrintError("x509: Extra arguments given.\nx509: Use -help for summary.\n");
ret = HITLS_APP_OPT_UNKOWN;
}
HITLS_APP_OptEnd();
return ret;
}
static int32_t GetCertPubkeyEncodeBuff(
HITLS_X509_Cert *cert, BSL_ParseFormat format, bool isComplete, BSL_Buffer *encode)
{
CRYPT_EAL_PkeyCtx *pubKey = NULL;
int32_t ret = HITLS_X509_CertCtrl(cert, HITLS_X509_GET_PUBKEY, &pubKey, 0);
if (ret != 0) {
AppPrintError("x509: Get pubKey from cert failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = CRYPT_EAL_EncodePubKeyBuffInternal(pubKey, format, CRYPT_PUBKEY_SUBKEY, isComplete, encode);
CRYPT_EAL_PkeyFreeCtx(pubKey);
if (ret != CRYPT_SUCCESS) {
AppPrintError("x509: Encode pubKey failed, errCode = %d.\n", ret);
return HITLS_APP_ENCODE_KEY_FAIL;
}
return HITLS_APP_SUCCESS;
}
* RFC 5280:
* section 4.1
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING
* }
* AlgorithmIdentifier ::= SEQUENCE { ... }
*
* section 4.2.1.2
* (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the
* BIT STRING subjectPublicKey (excluding the tag, length, and number of unused bits).
*/
static int32_t GetCertKid(HITLS_X509_Cert *cert, BSL_ParseFormat format, BSL_Buffer *buff)
{
BSL_Buffer info = {0};
int32_t ret = GetCertPubkeyEncodeBuff(cert, format, false, &info);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
uint8_t *enc = info.data;
uint32_t encLen = info.dataLen;
uint32_t vLen = 0;
ret = BSL_ASN1_DecodeTagLen(BSL_ASN1_TAG_CONSTRUCTED | BSL_ASN1_TAG_SEQUENCE, &enc, &encLen, &vLen);
if (ret != 0) {
AppPrintError("x509: Decode pubKey failed, errCode = %d.\n", ret);
ret = HITLS_APP_DECODE_FAIL;
goto EXIT;
}
enc += vLen;
encLen -= vLen;
ret = BSL_ASN1_DecodeTagLen(BSL_ASN1_TAG_BITSTRING, &enc, &encLen, &vLen);
if (ret != 0) {
AppPrintError("x509: Decode pubKey failed, errCode = %d.\n", ret);
ret = HITLS_APP_DECODE_FAIL;
goto EXIT;
}
enc += 1;
encLen -= 1;
buff->data = BSL_SAL_Malloc(20);
if (buff->data == NULL) {
AppPrintError("x509: Allocate memory for kid failed.\n");
ret = HITLS_APP_MEM_ALLOC_FAIL;
goto EXIT;
}
buff->dataLen = 20;
ret = CRYPT_EAL_Md(CRYPT_MD_SHA1, enc, encLen, buff->data, &buff->dataLen);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_FREE(buff->data);
buff->dataLen = 0;
AppPrintError("x509: Failed to calculate the kid, errCode = %d.\n", ret);
ret = HITLS_APP_CRYPTO_FAIL;
goto EXIT;
}
ret = HITLS_APP_SUCCESS;
EXIT:
BSL_SAL_Free(info.data);
return ret;
}
static int32_t LoadConf(X509OptCtx *optCtx)
{
if (optCtx->certOpts.extFile == NULL || optCtx->certOpts.extSection == NULL) {
return HITLS_APP_SUCCESS;
}
optCtx->conf = BSL_CONF_New(BSL_CONF_DefaultMethod());
if (optCtx->conf == NULL) {
AppPrintError("x509: New conf failed.\n");
return HITLS_APP_MEM_ALLOC_FAIL;
}
int32_t ret = BSL_CONF_Load(optCtx->conf, optCtx->certOpts.extFile);
if (ret != 0) {
BSL_CONF_Free(optCtx->conf);
optCtx->conf = NULL;
AppPrintError("x509: Load extfile %s failed, errCode = %d.\n", optCtx->certOpts.extFile, ret);
return HITLS_APP_CONF_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t LoadRelatedFiles(X509OptCtx *optCtx)
{
optCtx->csr = HITLS_APP_LoadCsr(optCtx->generalOpts.inPath, optCtx->generalOpts.inForm);
if (optCtx->csr == NULL) {
AppPrintError("x509: Load csr failed\n");
return HITLS_APP_LOAD_CSR_FAIL;
}
int32_t ret;
if (optCtx->userId != NULL) {
ret = HITLS_X509_CsrCtrl(optCtx->csr, HITLS_X509_SET_VFY_SM2_USER_ID, optCtx->userId, strlen(optCtx->userId));
if (ret != 0) {
AppPrintError("x509: set userId failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
ret = HITLS_X509_CsrVerify(optCtx->csr);
if (ret != 0) {
AppPrintError("x509: Verify csr failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
if (HITLS_APP_ParsePasswd(optCtx->generalOpts.passInArg, &optCtx->passin) != HITLS_APP_SUCCESS) {
return HITLS_APP_PASSWD_FAIL;
}
if (optCtx->certOpts.signKeyPath != NULL) {
optCtx->privKey = HITLS_APP_LoadPrvKey(optCtx->certOpts.signKeyPath, BSL_FORMAT_PEM, &optCtx->passin);
} else if (optCtx->certOpts.caKeyPath != NULL) {
optCtx->privKey = HITLS_APP_LoadPrvKey(optCtx->certOpts.caKeyPath, BSL_FORMAT_PEM, &optCtx->passin);
}
if (optCtx->privKey == NULL) {
AppPrintError("x509: Load signkey or cakey failed.\n");
return HITLS_APP_LOAD_KEY_FAIL;
}
if (optCtx->userId != NULL) {
ret = CRYPT_EAL_PkeyCtrl(optCtx->privKey, CRYPT_CTRL_SET_SM2_USER_ID, optCtx->userId, strlen(optCtx->userId));
if (ret != 0) {
AppPrintError("x509: set userId failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
if (optCtx->certOpts.caPath != NULL) {
optCtx->ca = HITLS_APP_LoadCert(optCtx->certOpts.caPath, BSL_FORMAT_PEM);
if (optCtx->ca == NULL) {
AppPrintError("x509: Load ca failed\n");
return HITLS_APP_LOAD_CERT_FAIL;
}
CRYPT_EAL_PkeyCtx *pubKey = NULL;
ret = HITLS_X509_CertCtrl(optCtx->ca, HITLS_X509_GET_PUBKEY, &pubKey, 0);
if (ret != 0) {
AppPrintError("x509: Get pubKey from ca failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = CRYPT_EAL_PkeyPairCheck(pubKey, optCtx->privKey);
CRYPT_EAL_PkeyFreeCtx(pubKey);
if (ret != 0) {
AppPrintError("x509: CA public key and CA private key do not match, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
return LoadConf(optCtx);
}
static int32_t SetSerial(X509OptCtx *optCtx)
{
int32_t ret;
if (optCtx->certOpts.serial == NULL) {
optCtx->certOpts.serial = BSL_SAL_Malloc(X509_DEFAULT_SERIAL_SIZE);
if (optCtx->certOpts.serial == NULL) {
AppPrintError("x509: Allocate serial memory failed.\n");
return HITLS_APP_MEM_ALLOC_FAIL;
}
optCtx->certOpts.serialLen = X509_DEFAULT_SERIAL_SIZE;
if ((ret = CRYPT_EAL_RandbytesEx(NULL, optCtx->certOpts.serial, optCtx->certOpts.serialLen)) != 0) {
BSL_SAL_FREE(optCtx->certOpts.serial);
AppPrintError("x509: Generate serial number failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
ret = HITLS_X509_CertCtrl(
optCtx->cert, HITLS_X509_SET_SERIALNUM, optCtx->certOpts.serial, optCtx->certOpts.serialLen);
if (ret != 0) {
AppPrintError("x509: Set serial number failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t SetValidity(X509OptCtx *optCtx)
{
int64_t startTime = BSL_SAL_CurrentSysTimeGet();
if (startTime <= 0) {
AppPrintError("x509: Get system time failed.\n");
return HITLS_APP_SAL_FAIL;
}
if (optCtx->certOpts.days > (INT64_MAX - startTime) / X509_DAY_SECONDS) {
AppPrintError("x509: The sum of the current time and -days %lld outside integer range.\n", optCtx->certOpts.days);
return HITLS_APP_SAL_FAIL;
}
int64_t endTime = startTime + optCtx->certOpts.days * X509_DAY_SECONDS;
if (endTime >= 253402272000) {
AppPrintError("x509: The end time of cert is greatter than 9999 years.\n");
return HITLS_APP_INVALID_ARG;
}
BSL_TIME start = {0};
BSL_TIME end = {0};
if (BSL_SAL_UtcTimeToDateConvert(startTime, &start) != 0 || BSL_SAL_UtcTimeToDateConvert(endTime, &end) != 0) {
AppPrintError("x509: Time convert failed.\n");
return HITLS_APP_SAL_FAIL;
}
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_BEFORE_TIME, &start, sizeof(BSL_TIME));
if (ret != 0) {
AppPrintError("x509: Set start time failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_AFTER_TIME, &end, sizeof(BSL_TIME));
if (ret != 0) {
AppPrintError("x509: Set end time failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t SetCertDn(X509OptCtx *optCtx)
{
BslList *subject = NULL;
int32_t ret = HITLS_X509_CsrCtrl(optCtx->csr, HITLS_X509_GET_SUBJECT_DN, &subject, sizeof(BslList *));
if (ret != 0) {
AppPrintError("x509: Get subject from csr failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_SUBJECT_DN, subject, sizeof(BslList));
if (ret != 0) {
AppPrintError("x509: Set subject failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
BslList *issuer = subject;
if (optCtx->ca != NULL) {
ret = HITLS_X509_CertCtrl(optCtx->ca, HITLS_X509_GET_SUBJECT_DN, &issuer, sizeof(BslList *));
if (ret != 0) {
AppPrintError("x509: Get subject from ca failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_ISSUER_DN, issuer, sizeof(BslList));
if (ret != 0) {
AppPrintError("x509: Set issuer failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t CopyExtensionsFromCsr(X509OptCtx *optCtx)
{
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_CSR_EXT, optCtx->csr, 0);
if (ret == HITLS_X509_ERR_ATTR_NOT_FOUND) {
return HITLS_APP_SUCCESS;
}
if (ret != 0) {
AppPrintError("x509: Copy csr extensions failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
int32_t version = HITLS_X509_VERSION_3;
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_VERSION, &version, sizeof(version));
if (ret != 0) {
AppPrintError("x509: Set cert version failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509SetBasicConstraints(HITLS_X509_ExtBCons *bCons, X509OptCtx *optCtx)
{
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_EXT_SET_BCONS, bCons, sizeof(HITLS_X509_ExtBCons));
if (ret != 0) {
AppPrintError("x509: Set basicConstraints failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509SetKeyUsage(HITLS_X509_ExtKeyUsage *ku, X509OptCtx *optCtx)
{
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_EXT_SET_KUSAGE, ku, sizeof(HITLS_X509_ExtKeyUsage));
if (ret != 0) {
AppPrintError("x509: Set keyUsage failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509SetExtendKeyUsage(HITLS_X509_ExtExKeyUsage *exku, X509OptCtx *optCtx)
{
int32_t ret =
HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_EXT_SET_EXKUSAGE, exku, sizeof(HITLS_X509_ExtExKeyUsage));
if (ret != 0) {
AppPrintError("x509: Set extendKeyUsage failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509SetSubjectAltName(HITLS_X509_ExtSan *san, X509OptCtx *optCtx)
{
int32_t ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_EXT_SET_SAN, san, sizeof(HITLS_X509_ExtSan));
if (ret != 0) {
AppPrintError("x509: Set subjectAltName failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509SetSubjectKeyIdentifier(HITLS_X509_ExtSki *ski, HITLS_X509_Cert *cert, bool needFree)
{
int32_t ret = GetCertKid(cert, BSL_FORMAT_ASN1, &ski->kid);
if (ret != 0) {
return ret;
}
ret = HITLS_X509_CertCtrl(cert, HITLS_X509_EXT_SET_SKI, ski, sizeof(HITLS_X509_ExtSki));
if (needFree) {
BSL_SAL_FREE(ski->kid.data);
}
if (ret != 0) {
AppPrintError("x509: Set subjectKeyIdentifier failed, errCode = %d.\n", ret);
BSL_SAL_FREE(ski->kid.data);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t SetSelfSignedCertAki(HITLS_CFG_ExtAki *cfgAki, HITLS_X509_Cert *cert)
{
bool isSkiExist;
HITLS_X509_ExtAki aki = cfgAki->aki;
HITLS_X509_ExtSki ski = {0};
int32_t ret = HITLS_X509_CertCtrl(cert, HITLS_X509_EXT_CHECK_SKI, &isSkiExist, sizeof(bool));
if (ret != 0) {
AppPrintError("x509: Check cert subjectKeyIdentifier failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
if (isSkiExist && (cfgAki->flag & HITLS_CFG_X509_EXT_AKI_KID_ALWAYS) == 0) {
return HITLS_APP_SUCCESS;
}
if (isSkiExist) {
ret = HITLS_X509_CertCtrl(cert, HITLS_X509_EXT_GET_SKI, &ski, sizeof(HITLS_X509_ExtSki));
if (ret != 0) {
AppPrintError("x509: Get cert subjectKeyIdentifier failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
} else {
ret = X509SetSubjectKeyIdentifier(&ski, cert, false);
if (ret != 0) {
return ret;
}
if ((cfgAki->flag & HITLS_CFG_X509_EXT_AKI_KID_ALWAYS) == 0) {
BSL_SAL_Free(ski.kid.data);
return ret;
}
}
aki.kid = ski.kid;
ret = HITLS_X509_CertCtrl(cert, HITLS_X509_EXT_SET_AKI, &aki, sizeof(HITLS_X509_ExtAki));
if (!isSkiExist) {
BSL_SAL_Free(ski.kid.data);
}
if (ret != 0) {
AppPrintError("x509: Set cert authorityKeyIdentifier failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t SetNonSelfSignedCertAki(HITLS_CFG_ExtAki *cfgAki, X509OptCtx *optCtx)
{
HITLS_X509_ExtSki caSki = {0};
HITLS_X509_ExtAki aki = cfgAki->aki;
bool isSkiExist;
int32_t ret = HITLS_X509_CertCtrl(optCtx->ca, HITLS_X509_EXT_GET_SKI, &caSki, sizeof(HITLS_X509_ExtSki));
if (ret != 0) {
AppPrintError("x509: Get issuer keyId failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
aki.kid = caSki.kid;
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_EXT_SET_AKI, &aki, sizeof(HITLS_X509_ExtAki));
if (ret != 0) {
AppPrintError("x509: Set non-self-signed cert authorityKeyIdentifier failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_EXT_CHECK_SKI, &isSkiExist, sizeof(bool));
if (ret != 0) {
AppPrintError("x509: Check cert subjectKeyIdentifier failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
if (isSkiExist) {
return HITLS_APP_SUCCESS;
}
return X509SetSubjectKeyIdentifier(&caSki, optCtx->cert, true);
}
static int32_t X509SetAuthKeyIdentifier(HITLS_CFG_ExtAki *cfgAki, X509OptCtx *optCtx)
{
if (optCtx->ca == NULL) {
return SetSelfSignedCertAki(cfgAki, optCtx->cert);
} else {
return SetNonSelfSignedCertAki(cfgAki, optCtx);
}
}
static int32_t X509ProcExt(BslCid cid, void *val, X509OptCtx *optCtx)
{
if (val == NULL) {
return HITLS_APP_INTERNAL_EXCEPTION;
}
switch (cid) {
case BSL_CID_CE_BASICCONSTRAINTS:
return X509SetBasicConstraints(val, optCtx);
case BSL_CID_CE_KEYUSAGE:
return X509SetKeyUsage(val, optCtx);
case BSL_CID_CE_EXTKEYUSAGE:
return X509SetExtendKeyUsage(val, optCtx);
case BSL_CID_CE_AUTHORITYKEYIDENTIFIER:
return X509SetAuthKeyIdentifier(val, optCtx);
case BSL_CID_CE_SUBJECTKEYIDENTIFIER:
return X509SetSubjectKeyIdentifier(val, optCtx->cert, true);
case BSL_CID_CE_SUBJECTALTNAME:
return X509SetSubjectAltName(val, optCtx);
default:
AppPrintError("x509: Unsupported extension: %d.\n", (int32_t)cid);
return HITLS_APP_X509_FAIL;
}
}
static int32_t SetCertExtensionsByConf(X509OptCtx *optCtx)
{
if (optCtx->conf == NULL) {
return HITLS_APP_SUCCESS;
}
int32_t ret =
HITLS_APP_CONF_ProcExt(optCtx->conf, optCtx->certOpts.extSection, (ProcExtCallBack)X509ProcExt, optCtx);
if (ret != HITLS_APP_SUCCESS && ret != HITLS_APP_NO_EXT) {
return ret;
}
if (ret == HITLS_APP_NO_EXT) {
return HITLS_APP_SUCCESS;
}
int32_t version = HITLS_X509_VERSION_3;
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_VERSION, &version, sizeof(version));
if (ret != 0) {
AppPrintError("x509: Set cert version failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t SetPubKey(X509OptCtx *optCtx)
{
int32_t ret;
CRYPT_EAL_PkeyCtx *pubKey = NULL;
if (optCtx->ca == NULL) {
pubKey = optCtx->privKey;
} else {
ret = HITLS_X509_CsrCtrl(optCtx->csr, HITLS_X509_GET_PUBKEY, &pubKey, 0);
if (ret != 0) {
AppPrintError("x509: Get pubKey from csr failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
ret = HITLS_X509_CertCtrl(optCtx->cert, HITLS_X509_SET_PUBKEY, pubKey, 0);
if (optCtx->ca != NULL) {
CRYPT_EAL_PkeyFreeCtx(pubKey);
}
if (ret != 0) {
AppPrintError("x509: Set public key failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t SetCertCont(X509OptCtx *optCtx)
{
int32_t ret = SetPubKey(optCtx);
if (ret != 0) {
return ret;
}
if (optCtx->certOpts.copyCsrExtensions) {
ret = CopyExtensionsFromCsr(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
}
ret = SetCertExtensionsByConf(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
ret = SetSerial(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
ret = SetValidity(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
return SetCertDn(optCtx);
}
static int32_t GenCert(X509OptCtx *optCtx)
{
int32_t ret = LoadRelatedFiles(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
optCtx->cert = HITLS_X509_CertNew();
if (optCtx->cert == NULL) {
AppPrintError("x509: Failed to new a cert.\n");
return HITLS_APP_X509_FAIL;
}
ret = SetCertCont(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
ret = HITLS_X509_CertSign(optCtx->certOpts.mdId, optCtx->privKey, NULL, optCtx->cert);
if (ret != 0) {
AppPrintError("x509: sign cert failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = HITLS_X509_CertGenBuff(optCtx->generalOpts.outForm, optCtx->cert, &optCtx->encodeCert);
if (ret != 0) {
AppPrintError("x509: encode cert failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t LoadCert(X509OptCtx *optCtx)
{
optCtx->cert = HITLS_APP_LoadCert(optCtx->generalOpts.inPath, optCtx->generalOpts.inForm);
if (optCtx->cert == NULL) {
return HITLS_APP_LOAD_CERT_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t OutputPubkey(X509OptCtx *optCtx)
{
if (!optCtx->printOpts.pubKey) {
return HITLS_APP_SUCCESS;
}
BSL_Buffer encodePubkey = {0};
int32_t ret = GetCertPubkeyEncodeBuff(optCtx->cert, BSL_FORMAT_PEM, true, &encodePubkey);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
uint32_t writeLen = 0;
ret = BSL_UIO_Write(optCtx->outUio, encodePubkey.data, encodePubkey.dataLen, &writeLen);
BSL_SAL_Free(encodePubkey.data);
if (ret != 0 || writeLen != encodePubkey.dataLen) {
AppPrintError("x509: write pubKey failed, errCode = %d, writeLen = %u.\n", ret, writeLen);
return HITLS_APP_UIO_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t X509Output(X509OptCtx *optCtx)
{
int32_t ret;
optCtx->outUio = HITLS_APP_UioOpen(optCtx->generalOpts.outPath, 'w',
optCtx->generalOpts.outPath != NULL ? 1 : 0);
if (optCtx->outUio == NULL) {
return HITLS_APP_UIO_FAIL;
}
if (optCtx->printOpts.issuer || optCtx->printOpts.subject || optCtx->printOpts.text) {
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_SET_PRINT_FLAG, (void *)&optCtx->printOpts.nameOpt, sizeof(int32_t), NULL);
if (ret != 0) {
AppPrintError("x509: Set DN print flag failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
ret = AppPrintX509(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
ret = OutputPubkey(optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
if (optCtx->generalOpts.noout) {
return HITLS_APP_SUCCESS;
}
if (optCtx->encodeCert.data == NULL) {
ret = HITLS_X509_CertGenBuff(optCtx->generalOpts.outForm, optCtx->cert, &optCtx->encodeCert);
if (ret != 0) {
AppPrintError("x509: encode cert failed, errCode = %d.\n", ret);
return HITLS_APP_X509_FAIL;
}
}
uint32_t writeLen = 0;
ret = BSL_UIO_Write(optCtx->outUio, optCtx->encodeCert.data, optCtx->encodeCert.dataLen, &writeLen);
if (ret != 0 || writeLen != optCtx->encodeCert.dataLen) {
AppPrintError("x509: write cert failed, errCode = %d, writeLen = %u.\n", ret, writeLen);
return HITLS_APP_UIO_FAIL;
}
return HITLS_APP_SUCCESS;
}
static bool CheckGenCertOpt(X509OptCtx *optCtx)
{
if (optCtx->certOpts.caPath != NULL) {
if (optCtx->certOpts.signKeyPath != NULL) {
AppPrintError("x509: Cannot use both -signkey and -CA.\n");
return false;
}
} else {
if (optCtx->certOpts.caKeyPath != NULL) {
if (optCtx->certOpts.signKeyPath != NULL) {
AppPrintError("x509: Cannot use both -CAkey and -signkey.\n");
return false;
} else {
AppPrintError("x509: Should use both -CA and -CAkey.\n");
return false;
}
}
}
if (optCtx->certOpts.signKeyPath == NULL && optCtx->certOpts.caKeyPath == NULL) {
AppPrintError("x509: We need a private key to genetate cert, use -signkey or -CAkey.\n");
return false;
}
if (optCtx->certOpts.extFile != NULL && optCtx->certOpts.extSection == NULL) {
AppPrintError("x509: Warning: ignoring -extFile since -extensions is not given.\n");
optCtx->certOpts.extFile = NULL;
}
if (optCtx->certOpts.extFile == NULL && optCtx->certOpts.extSection != NULL) {
AppPrintError("x509: Warning: ignoring -extensions since -extFile is not given.\n");
optCtx->certOpts.extSection = NULL;
}
return true;
}
static bool CheckOpt(X509OptCtx *optCtx)
{
if (optCtx->generalOpts.req) {
return CheckGenCertOpt(optCtx);
} else {
if (optCtx->certOpts.signKeyPath != NULL || optCtx->certOpts.caKeyPath != NULL ||
optCtx->certOpts.caPath != NULL) {
AppPrintError("x509: Warning: ignoring -signkey, -CA, -CAkey since -req is not given.\n");
optCtx->certOpts.caKeyPath = NULL;
optCtx->certOpts.signKeyPath = NULL;
optCtx->certOpts.caPath = NULL;
}
if (optCtx->certOpts.serialLen != 0) {
AppPrintError("x509: Warning: ignoring -set_serial since -req is not given.\n");
BSL_SAL_FREE(optCtx->certOpts.serial);
optCtx->certOpts.serialLen = 0;
}
if (optCtx->certOpts.extFile != NULL || optCtx->certOpts.extSection != NULL) {
AppPrintError("x509: Warning: ignoring -extfile or -extensions since -req is not given.\n");
optCtx->certOpts.extFile = NULL;
optCtx->certOpts.extSection = NULL;
}
if (optCtx->certOpts.copyCsrExtensions) {
AppPrintError("x509: Warning: ignoring -copy_extensions since -req is not given.\n");
optCtx->certOpts.copyCsrExtensions = false;
}
}
return true;
}
int32_t HandleX509Opt(int argc, char *argv[], X509OptCtx *optCtx)
{
int32_t ret = ParseX509Opt(argc, argv, optCtx);
if (ret != HITLS_APP_SUCCESS) {
return ret;
}
if (!CheckOpt(optCtx)) {
return HITLS_APP_OPT_TYPE_INVALID;
}
return HITLS_APP_SUCCESS;
}
static void InitX509OptCtx(X509OptCtx *optCtx)
{
optCtx->generalOpts.inForm = BSL_FORMAT_PEM;
optCtx->generalOpts.outForm = BSL_FORMAT_PEM;
optCtx->generalOpts.req = false;
optCtx->generalOpts.noout = false;
optCtx->printOpts.nameOpt = HITLS_PKI_PRINT_DN_ONELINE;
optCtx->certOpts.days = X509_DEFAULT_CERT_DAYS;
optCtx->certOpts.mdId = CRYPT_MD_SHA256;
optCtx->printOpts.mdId = CRYPT_MD_SHA1;
}
static void UnInitX509OptCtx(X509OptCtx *optCtx)
{
BSL_UIO_Free(optCtx->outUio);
optCtx->outUio = NULL;
BSL_CONF_Free(optCtx->conf);
optCtx->conf = NULL;
HITLS_X509_CertFree(optCtx->cert);
optCtx->cert = NULL;
HITLS_X509_CertFree(optCtx->ca);
optCtx->ca = NULL;
HITLS_X509_CsrFree(optCtx->csr);
optCtx->csr = NULL;
CRYPT_EAL_PkeyFreeCtx(optCtx->privKey);
optCtx->privKey = NULL;
BSL_SAL_FREE(optCtx->certOpts.serial);
BSL_SAL_FREE(optCtx->encodeCert.data);
if (optCtx->passin != NULL) {
BSL_SAL_ClearFree(optCtx->passin, strlen(optCtx->passin));
}
}
int32_t HITLS_X509Main(int argc, char *argv[])
{
ResetPrintX509FuncList();
X509OptCtx optCtx = {0};
InitX509OptCtx(&optCtx);
int32_t ret = HITLS_APP_SUCCESS;
do {
ret = HandleX509Opt(argc, argv, &optCtx);
if (ret != HITLS_APP_SUCCESS) {
break;
}
if (optCtx.generalOpts.req) {
if (CRYPT_EAL_ProviderRandInitCtx(NULL, CRYPT_RAND_AES128_CTR,
"provider=default", NULL, 0, NULL) != CRYPT_SUCCESS) {
ret = HITLS_APP_CRYPTO_FAIL;
break;
}
ret = GenCert(&optCtx);
} else {
ret = LoadCert(&optCtx);
}
if (ret != HITLS_APP_SUCCESS) {
break;
}
ret = X509Output(&optCtx);
} while (false);
UnInitX509OptCtx(&optCtx);
CRYPT_EAL_RandDeinitEx(NULL);
return ret;
}