blob: 781abe0e23c3757e7e4f563e8421318f29fa271f [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements the command handler for the 'chip-cert' tool
* that re-signs a CHIP certificate.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include "chip-cert.h"
namespace {
using namespace chip::ArgParser;
using namespace chip::Credentials;
using namespace chip::ASN1;
#define CMD_NAME "chip-cert resign-cert"
bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg);
// clang-format off
OptionDef gCmdOptionDefs[] =
{
{ "cert", kArgumentRequired, 'c' },
{ "out", kArgumentRequired, 'o' },
{ "ca-cert", kArgumentRequired, 'C' },
{ "ca-key", kArgumentRequired, 'K' },
{ "self", kNoArgument, 's' },
{ }
};
const char * const gCmdOptionHelp =
" -c, --cert <file>\n"
"\n"
" File containing the certificate to be re-signed.\n"
"\n"
" -o, --out <file>\n"
"\n"
" File to contain the re-signed certificate.\n"
"\n"
" -C, --ca-cert <file>\n"
"\n"
" File containing CA certificate to be used to re-sign the certificate.\n"
"\n"
" -K, --ca-key <file>\n"
"\n"
" File containing CA private key to be used to re-sign the certificate.\n"
"\n"
" -s, --self\n"
"\n"
" Generate a self-signed certificate.\n"
"\n"
;
OptionSet gCmdOptions =
{
HandleOption,
gCmdOptionDefs,
"COMMAND OPTIONS",
gCmdOptionHelp
};
HelpOptions gHelpOptions(
CMD_NAME,
"Usage: " CMD_NAME " [ <options...> ]\n",
CHIP_VERSION_STRING "\n" COPYRIGHT_STRING,
"Resign a CHIP certificate using a new CA certificate/key."
);
OptionSet * gCmdOptionSets[] =
{
&gCmdOptions,
&gHelpOptions,
nullptr
};
// clang-format on
const char * gInCertFileName = nullptr;
const char * gOutCertFileName = nullptr;
const char * gCACertFileName = nullptr;
const char * gCAKeyFileName = nullptr;
bool gSelfSign = false;
bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg)
{
switch (id)
{
case 'c':
gInCertFileName = arg;
break;
case 'o':
gOutCertFileName = arg;
break;
case 'C':
gCACertFileName = arg;
break;
case 'K':
gCAKeyFileName = arg;
break;
case 's':
gSelfSign = true;
break;
default:
PrintArgError("%s: Unhandled option: %s\n", progName, name);
return false;
}
return true;
}
} // namespace
bool Cmd_ResignCert(int argc, char * argv[])
{
bool res = true;
CertFormat inCertFmt;
std::unique_ptr<X509, void (*)(X509 *)> cert(X509_new(), &X509_free);
std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)> caKey(EVP_PKEY_new(), &EVP_PKEY_free);
if (argc == 1)
{
gHelpOptions.PrintBriefUsage(stderr);
ExitNow(res = true);
}
res = ParseArgs(CMD_NAME, argc, argv, gCmdOptionSets);
VerifyTrueOrExit(res);
if (gInCertFileName == nullptr)
{
fprintf(stderr, "Please specify certificate to be resigned using --cert option.\n");
ExitNow(res = false);
}
if (gOutCertFileName == nullptr)
{
fprintf(stderr, "Please specify the file name for the new certificate using the --out option.\n");
ExitNow(res = false);
}
if (gCACertFileName == nullptr && !gSelfSign)
{
fprintf(stderr,
"Please specify a CA certificate to be used to sign the new certificate (using\n"
"the --ca-cert option) or --self to generate a self-signed certificate.\n");
ExitNow(res = false);
}
else if (gCACertFileName != nullptr && gSelfSign)
{
fprintf(stderr, "Please specify only one of --ca-cert and --self.\n");
ExitNow(res = false);
}
if (gCAKeyFileName == nullptr)
{
fprintf(stderr, "Please specify the CA key file name using the --ca-key option.\n");
ExitNow(res = false);
}
if (access(gOutCertFileName, R_OK) == 0)
{
fprintf(stderr,
"Output certificate file already exists (%s)\n"
"To replace the file, please remove it and re-run the command.\n",
gOutCertFileName);
ExitNow(res = false);
}
res = InitOpenSSL();
VerifyTrueOrExit(res);
res = ReadCert(gInCertFileName, cert.get(), inCertFmt);
VerifyTrueOrExit(res);
res = ReadKey(gCAKeyFileName, caKey.get());
VerifyTrueOrExit(res);
if (!gSelfSign)
{
std::unique_ptr<X509, void (*)(X509 *)> caCert(X509_new(), &X509_free);
res = ReadCert(gCACertFileName, caCert.get());
VerifyTrueOrExit(res);
res = ResignCert(cert.get(), caCert.get(), caKey.get());
VerifyTrueOrExit(res);
}
else
{
res = ResignCert(cert.get(), cert.get(), caKey.get());
VerifyTrueOrExit(res);
}
res = WriteCert(gOutCertFileName, cert.get(), inCertFmt);
VerifyTrueOrExit(res);
exit:
return res;
}