-
Notifications
You must be signed in to change notification settings - Fork 134
Adding -verify and expanding -x509 options for our OpenSSL tool #1951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
4b453f2
x509 subject and fingerprint code
smittals2 1386b77
setting up verify tooling
smittals2 930252b
wip verify
smittals2 93166b6
added verification functionality
smittals2 9904e13
made changes to test certificate creation function to make it valid s…
smittals2 662ba1a
verify tool testing and minor changes
smittals2 572bf97
added comment
smittals2 9b52d65
Merge branch 'aws:main' into openssl-tool
smittals2 ebae340
added tests and freed leaking memory
smittals2 62f2158
comment fix
smittals2 1a63ee0
added func to normalize subject string to match openssl master
smittals2 1507288
Merge branch 'main' into openssl-tool
smittals2 8e25994
added -inform and -enddate with testing to x509 tool
smittals2 19ca2f7
refactored to read from stdin, added test, changed order of printing …
smittals2 4071dd3
added subject_hash and subject_hash_old
smittals2 d1ce560
added additional checks and changed NULL to nullptr
smittals2 9043a2d
addressed PR comments
smittals2 a5a543e
pr comments
smittals2 c09eec3
removed option to configure default trust stroe
smittals2 233b677
Merge branch 'main' into openssl-tool
smittals2 242a4f0
changed spacing
smittals2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 OR ISC | ||
|
||
#include <openssl/base.h> | ||
#include <openssl/x509.h> | ||
#include <openssl/pem.h> | ||
#include "internal.h" | ||
|
||
// TO-DO: We do not support using a default trust store, therefore -CAfile must | ||
// be a required argument. Once support for default trust stores is added, | ||
// make it an optional argument. | ||
static const argument_t kArguments[] = { | ||
{ "-help", kBooleanArgument, "Display option summary" }, | ||
{ "-CAfile", kRequiredArgument, "A file of trusted certificates. The " | ||
"file should contain one or more certificates in PEM format." }, | ||
{ "", kOptionalArgument, "" } | ||
}; | ||
|
||
// setup_verification_store sets up an X509 certificate store for verification. | ||
// It configures the store with file and directory lookups. It loads the | ||
// specified CA file if provided and otherwise uses default locations. | ||
smittals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
static X509_STORE *setup_verification_store(std::string CAfile) { | ||
bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | ||
X509_LOOKUP *lookup; | ||
|
||
if (!store) { | ||
return nullptr; | ||
} | ||
|
||
if (!CAfile.empty()) { | ||
lookup = X509_STORE_add_lookup(store.get(), X509_LOOKUP_file()); | ||
if (!lookup || !X509_LOOKUP_load_file(lookup, CAfile.c_str(), X509_FILETYPE_PEM)) { | ||
fprintf(stderr, "Error loading file %s\n", CAfile.c_str()); | ||
smittals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nullptr; | ||
} | ||
} | ||
|
||
// Add default dir path | ||
lookup = X509_STORE_add_lookup(store.get(), X509_LOOKUP_hash_dir()); | ||
if (!lookup || !X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT)) { | ||
return nullptr; | ||
} | ||
|
||
return store.release(); | ||
} | ||
|
||
static int cb(int ok, X509_STORE_CTX *ctx) { | ||
if (!ok) { | ||
int cert_error = X509_STORE_CTX_get_error(ctx); | ||
X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); | ||
|
||
if (current_cert != NULL) { | ||
X509_NAME_print_ex_fp(stderr, | ||
X509_get_subject_name(current_cert), | ||
0, XN_FLAG_ONELINE); | ||
fprintf(stderr, "\n"); | ||
} | ||
fprintf(stderr, "%serror %d at %d depth lookup: %s\n", | ||
X509_STORE_CTX_get0_parent_ctx(ctx) ? "[CRL path] " : "", | ||
cert_error, | ||
X509_STORE_CTX_get_error_depth(ctx), | ||
X509_verify_cert_error_string(cert_error)); | ||
|
||
/* | ||
* Pretend that some errors are ok, so they don't stop further | ||
* processing of the certificate chain. Setting ok = 1 does this. | ||
* After X509_verify_cert() is done, we verify that there were | ||
* no actual errors, even if the returned value was positive. | ||
*/ | ||
switch (cert_error) { | ||
smittals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
case X509_V_ERR_NO_EXPLICIT_POLICY: | ||
/* fall thru */ | ||
case X509_V_ERR_CERT_HAS_EXPIRED: | ||
/* Continue even if the leaf is a self-signed cert */ | ||
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: | ||
/* Continue after extension errors too */ | ||
case X509_V_ERR_INVALID_CA: | ||
case X509_V_ERR_INVALID_NON_CA: | ||
case X509_V_ERR_PATH_LENGTH_EXCEEDED: | ||
case X509_V_ERR_CRL_HAS_EXPIRED: | ||
case X509_V_ERR_CRL_NOT_YET_VALID: | ||
case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: | ||
/* errors due to strict conformance checking (-x509_strict) */ | ||
case X509_V_ERR_INVALID_PURPOSE: | ||
ok = 1; | ||
} | ||
} | ||
return ok; | ||
} | ||
|
||
static int check(X509_STORE *ctx, const char *file) { | ||
bssl::UniquePtr<X509> cert; | ||
int i = 0, ret = 0; | ||
|
||
if (file) { | ||
ScopedFILE cert_file(fopen(file, "rb")); | ||
if (!cert_file) { | ||
fprintf(stderr, "error %s: reading certificate failed\n", file); | ||
smittals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return 0; | ||
} | ||
cert.reset(PEM_read_X509(cert_file.get(), nullptr, nullptr, nullptr)); | ||
|
||
} else { | ||
bssl::UniquePtr<BIO> input(BIO_new_fp(stdin, BIO_CLOSE)); | ||
cert.reset(PEM_read_bio_X509(input.get(), nullptr, nullptr, nullptr)); | ||
} | ||
|
||
if (cert.get() == nullptr) { | ||
return 0; | ||
} | ||
|
||
bssl::UniquePtr<X509_STORE_CTX> store_ctx(X509_STORE_CTX_new()); | ||
if (store_ctx == nullptr || store_ctx.get() == nullptr) { | ||
fprintf(stderr, "error %s: X.509 store context allocation failed\n", | ||
(file == nullptr) ? "stdin" : file); | ||
return 0; | ||
} | ||
|
||
if (!X509_STORE_CTX_init(store_ctx.get(), ctx, cert.get(), nullptr)) { | ||
fprintf(stderr, | ||
"error %s: X.509 store context initialization failed\n", | ||
(file == nullptr) ? "stdin" : file); | ||
return 0; | ||
} | ||
|
||
i = X509_verify_cert(store_ctx.get()); | ||
if (i > 0 && X509_STORE_CTX_get_error(store_ctx.get()) == X509_V_OK) { | ||
fprintf(stdout, "%s: OK\n", (file == nullptr) ? "stdin" : file); | ||
ret = 1; | ||
} else { | ||
fprintf(stderr, | ||
"error %s: verification failed\n", | ||
(file == nullptr) ? "stdin" : file); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
bool VerifyTool(const args_list_t &args) { | ||
std::string cafile; | ||
size_t i = 0; | ||
|
||
if (args.size() == 1 && args[0] == "-help") { | ||
fprintf(stderr, | ||
"Usage: verify [options] [cert.pem...]\n" | ||
"Certificates must be in PEM format. They can be specified in one or more files.\n" | ||
"If no files are specified, the tool will read from stdin.\n\n" | ||
"Valid options are:\n"); | ||
PrintUsage(kArguments); | ||
return false; | ||
} | ||
|
||
// i helps track whether input will be provided via stdin or through a file | ||
if (args.size() >= 1 && args[0] == "-CAfile") { | ||
cafile = args[1]; | ||
i += 2; | ||
andrewhop marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
fprintf(stderr, "-CAfile must be specified. This tool does not load" | ||
" the default trust store.\n"); | ||
return false; | ||
} | ||
|
||
bssl::UniquePtr<X509_STORE> store(setup_verification_store(cafile)); | ||
// Initialize certificate verification store | ||
if (!store.get()) { | ||
fprintf(stderr, "Error: Unable to setup certificate verification store."); | ||
return false; | ||
} | ||
X509_STORE_set_verify_cb(store.get(), cb); | ||
|
||
ERR_clear_error(); | ||
|
||
int ret = 1; | ||
|
||
// No additional file or certs provided, read from stdin | ||
if (args.size() == i) { | ||
ret &= check(store.get(), NULL); | ||
} else { | ||
// Certs provided as files | ||
for (; i < args.size(); i++) { | ||
ret &= check(store.get(), args[i].c_str()); | ||
} | ||
} | ||
|
||
return ret == 1; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 OR ISC | ||
|
||
#include "openssl/x509.h" | ||
#include <gtest/gtest.h> | ||
#include <openssl/pem.h> | ||
#include "internal.h" | ||
#include "test_util.h" | ||
#include "../crypto/test/test_util.h" | ||
|
||
|
||
class VerifyTest : public ::testing::Test { | ||
protected: | ||
void SetUp() override { | ||
ASSERT_GT(createTempFILEpath(ca_path), 0u); | ||
ASSERT_GT(createTempFILEpath(in_path), 0u); | ||
|
||
bssl::UniquePtr<X509> x509(CreateAndSignX509Certificate()); | ||
ASSERT_TRUE(x509); | ||
|
||
ScopedFILE in_file(fopen(in_path, "wb")); | ||
ASSERT_TRUE(in_file); | ||
ASSERT_TRUE(PEM_write_X509(in_file.get(), x509.get())); | ||
|
||
ScopedFILE ca_file(fopen(ca_path, "wb")); | ||
ASSERT_TRUE(ca_file); | ||
ASSERT_TRUE(PEM_write_X509(ca_file.get(), x509.get())); | ||
} | ||
void TearDown() override { | ||
RemoveFile(ca_path); | ||
RemoveFile(in_path); | ||
} | ||
char ca_path[PATH_MAX]; | ||
char in_path[PATH_MAX]; | ||
}; | ||
|
||
|
||
// ----------------------------- Verify Option Tests ----------------------------- | ||
|
||
// Test -CAfile with self-signed certificate | ||
TEST_F(VerifyTest, VerifyTestSelfSignedCertWithCAfileTest) { | ||
args_list_t args = {"-CAfile", ca_path, in_path}; | ||
bool result = VerifyTool(args); | ||
ASSERT_TRUE(result); | ||
} | ||
|
||
// Test certificate without -CAfile | ||
TEST_F(VerifyTest, VerifyTestSelfSignedCertWithoutCAfile) { | ||
args_list_t args = {in_path}; | ||
bool result = VerifyTool(args); | ||
ASSERT_FALSE(result); | ||
} | ||
|
||
|
||
// -------------------- Verify OpenSSL Comparison Tests -------------------------- | ||
|
||
// Comparison tests cannot run without set up of environment variables: | ||
// AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. | ||
|
||
class VerifyComparisonTest : public ::testing::Test { | ||
protected: | ||
void SetUp() override { | ||
andrewhop marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Skip gtests if env variables not set | ||
tool_executable_path = getenv("AWSLC_TOOL_PATH"); | ||
openssl_executable_path = getenv("OPENSSL_TOOL_PATH"); | ||
if (tool_executable_path == nullptr || openssl_executable_path == nullptr) { | ||
GTEST_SKIP() << "Skipping test: AWSLC_TOOL_PATH and/or OPENSSL_TOOL_PATH environment variables are not set"; | ||
} | ||
|
||
ASSERT_GT(createTempFILEpath(in_path), 0u); | ||
ASSERT_GT(createTempFILEpath(ca_path), 0u); | ||
ASSERT_GT(createTempFILEpath(out_path_tool), 0u); | ||
ASSERT_GT(createTempFILEpath(out_path_openssl), 0u); | ||
|
||
x509.reset(CreateAndSignX509Certificate()); | ||
ASSERT_TRUE(x509); | ||
|
||
ScopedFILE in_file(fopen(in_path, "wb")); | ||
ASSERT_TRUE(in_file); | ||
ASSERT_TRUE(PEM_write_X509(in_file.get(), x509.get())); | ||
|
||
ScopedFILE ca_file(fopen(ca_path, "wb")); | ||
ASSERT_TRUE(ca_file); | ||
ASSERT_TRUE(PEM_write_X509(ca_file.get(), x509.get())); | ||
} | ||
|
||
void TearDown() override { | ||
if (tool_executable_path != nullptr && openssl_executable_path != nullptr) { | ||
RemoveFile(in_path); | ||
RemoveFile(out_path_tool); | ||
RemoveFile(out_path_openssl); | ||
RemoveFile(ca_path); | ||
} | ||
} | ||
|
||
char in_path[PATH_MAX]; | ||
char ca_path[PATH_MAX]; | ||
char out_path_tool[PATH_MAX]; | ||
char out_path_openssl[PATH_MAX]; | ||
bssl::UniquePtr<X509> x509; | ||
const char* tool_executable_path; | ||
const char* openssl_executable_path; | ||
std::string tool_output_str; | ||
std::string openssl_output_str; | ||
}; | ||
|
||
// Test against OpenSSL with -CAfile & self-signed cert fed in as a file | ||
smittals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// "openssl verify -CAfile cert.pem cert.pem" | ||
TEST_F(VerifyComparisonTest, VerifyToolOpenSSLCAFileSelfSignedComparison) { | ||
std::string tool_command = std::string(tool_executable_path) + " verify -CAfile " + ca_path + " " + in_path + " &> " + out_path_tool; | ||
std::string openssl_command = std::string(openssl_executable_path) + " verify -CAfile " + ca_path + " " + in_path + " &> " + out_path_openssl; | ||
|
||
RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); | ||
|
||
ASSERT_EQ(tool_output_str, openssl_output_str); | ||
} | ||
|
||
// Test against OpenSSL with -CAfile & 2 self-signed cert fed in as files | ||
// "openssl verify -CAfile cert.pem cert.pem cert.pem" | ||
TEST_F(VerifyComparisonTest, VerifyToolOpenSSLCAFileMultipleFilesComparison) { | ||
std::string tool_command = std::string(tool_executable_path) + " verify -CAfile " + ca_path + " " + in_path + " " + in_path + " &> " + out_path_tool; | ||
std::string openssl_command = std::string(openssl_executable_path) + " verify -CAfile " + ca_path + " " + in_path + " " + in_path + " &> " + out_path_openssl; | ||
|
||
RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); | ||
|
||
ASSERT_EQ(tool_output_str, openssl_output_str); | ||
} | ||
|
||
// Test against OpenSSL with -CAfile & self-signed cert fed through stdin | ||
// "cat cert.pem | openssl verify -CAfile cert.pem" | ||
TEST_F(VerifyComparisonTest, VerifyToolOpenSSLCAFileSelfSignedStdinComparison) { | ||
std::string tool_command = "cat " + std::string(ca_path) + " | " + std::string(tool_executable_path) + " verify -CAfile " + ca_path + " &> " + out_path_tool; | ||
std::string openssl_command = "cat " + std::string(ca_path) + " | " + std::string(openssl_executable_path) + " verify -CAfile " + ca_path + " &> " + out_path_openssl; | ||
|
||
RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); | ||
|
||
ASSERT_EQ(tool_output_str, openssl_output_str); | ||
} | ||
smittals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.