Skip to content

Implement pkcs8 cli #2342

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

Open
wants to merge 62 commits into
base: main
Choose a base branch
from
Open

Implement pkcs8 cli #2342

wants to merge 62 commits into from

Conversation

kingstjo
Copy link
Contributor

Issues:

Addresses CryptoAlg-3073
Addresses CryptoAlg-3074
Addresses CryptoAlg-3075

Description of changes:

This PR implements the PKCS8 CLI command wrapper for AWS-LC, providing OpenSSL-compatible command-line interface to the existing PKCS8 functionality. The CLI tool adds support for:

  1. Converting traditional format private keys to PKCS#8 format through a command-line interface
  2. CLI options for both encrypted and unencrypted PKCS#8 private keys
  3. Command-line access to PKCS#5 v2.0 encryption algorithms
  4. Handling PRF parameters through AWS-LC's existing implementation
  5. Supporting multiple key types via d2i_AutoPrivateKey wrapper
  6. DER and PEM format input/output options

Call-outs:

  1. PRF Parameter Compatibility: The implementation accepts the -v2prf parameter for OpenSSL command-line compatibility and validates supported values (hmacWithSHA1, hmacWithSHA256, hmacWithSHA512). However, AWS-LC's underlying PKCS#8 implementation always uses its default PRF implementation (-1) in the PKCS8_encrypt function, regardless of the specified -v2prf value.

  2. Comprehensive Key Type Support: The CLI implementation uses d2i_AutoPrivateKey to provide support for all private key types that AWS-LC can handle. This allows users to process various key formats through the pkcs8 command without type restrictions.

  3. User-Friendly Error Handling: The implementation provides context-rich error messages that guide users through troubleshooting common issues including:

    • Invalid key types or formats
    • Password/passphrase problems
    • Permission or disk space issues for output files
    • Unsupported encryption parameters

Testing:

The CLI wrapper is validated through two testing approaches:

  1. Unit tests (PKCS8Test class) verify:

    • Basic CLI options processing (PKCS8ToolBasicTest)
    • Format handling with -inform and -outform options (PKCS8ToolFormatTest)
    • Encryption with -v2 aes-256-cbc options (PKCS8ToolEncryptionTest)
    • PRF parameter handling via -v2prf (PKCS8ToolPRFTest)
    • Error handling for invalid inputs (PKCS8OptionUsageErrorsTest)
  2. Comparison tests (PKCS8ComparisonTest class) directly compare AWS-LC's output with OpenSSL's:

    • Unencrypted PKCS#8 conversion (PKCS8ToolCompareUnencryptedOpenSSL)
    • Encrypted PKCS#8 with AES-256-CBC (PKCS8ToolCompareEncryptedOpenSSL)
    • DER format output handling (PKCS8ToolCompareDERFormatOpenSSL)
    • PRF parameter handling (PKCS8ToolCompareV2prfOpenSSL)

These tests ensure that the AWS-LC CLI tool produces output files with the correct PKCS#8 boundaries and format compatibility with OpenSSL's pkcs8 command.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

Jonathon Kingston and others added 8 commits April 17, 2025 21:12
Add the pkcs8 command to the AWS-LC OpenSSL-compatible CLI tools.
This implementation includes support for the following options:
- [-in filename] [-out filename]
- [-inform format] [-outform format]
- [-topk8] [-nocrypt]
- [-v2 alg] [-v2prf alg]
- [-passin arg] [-passout arg]

The implementation ensures identical output to OpenSSL 1.1.1 and
includes appropriate test files. This functionality allows users
to convert between traditional key formats and PKCS#8 with
options for encryption, format control, and password handling.
This fix addresses three main issues in the PKCS8 CLI tool:
1) Properly handle PRF parameters with AWS-LC's implementation
2) Support all key types by using d2i_AutoPrivateKey instead of RSA-specific function
3) Add more descriptive error messages for better troubleshooting
@codecov-commenter
Copy link

codecov-commenter commented Apr 21, 2025

Codecov Report

Attention: Patch coverage is 44.21907% with 275 lines in your changes missing coverage. Please review.

Project coverage is 78.74%. Comparing base (9bfdaf0) to head (4489b3c).
Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
tool-openssl/pkcs8_test.cc 31.90% 175 Missing ⚠️
tool-openssl/pkcs8.cc 59.64% 92 Missing ⚠️
tool-openssl/test_util.h 0.00% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2342      +/-   ##
==========================================
- Coverage   78.81%   78.74%   -0.07%     
==========================================
  Files         621      623       +2     
  Lines      108431   109106     +675     
  Branches    15399    15473      +74     
==========================================
+ Hits        85460    85919     +459     
- Misses      22300    22518     +218     
+ Partials      671      669       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kingstjo kingstjo force-pushed the implement-pkcs8-cli branch from 1d59317 to fe370f1 Compare April 22, 2025 20:53
@kingstjo kingstjo marked this pull request as ready for review April 23, 2025 17:07
@kingstjo kingstjo requested a review from a team as a code owner April 23, 2025 17:07
@kingstjo kingstjo force-pushed the implement-pkcs8-cli branch from ab578cf to fe370f1 Compare April 24, 2025 21:27
kingstjo and others added 6 commits April 24, 2025 15:06
- Updated the implementation to only accept hmacWithSHA1 as a valid PRF algorithm
- Added explicit error messages when users specify unsupported PRF algorithms
- Updated comments to clearly indicate that only hmacWithSHA1 is supported
- Updated tests to use hmacWithSHA1 and added test for unsupported PRF rejection
Added OpenSSLPointer template in internal.h to handle memory allocated with OPENSSL functions.
Updated pkcs8.cc to use smart pointers for better resource management and to eliminate manual cleanup.
This improves code safety, simplifies error paths, and aligns with patterns used in the rest of the codebase.
When -v2 option is specified without a value in OpenSSL 1.1.1, it defaults to using aes-256-cbc. This commit ensures AWS-LC maintains the same behavior for compatibility. Added tests to verify this behavior.
- Added cross-compatibility tests to verify AWS-LC and OpenSSL can decrypt each other's outputs
- Added tests for custom PRF algorithm and default cipher interoperability
- Moved helper functions to test_util.h for better code organization
- Enhanced test validation to verify decrypted keys match original keys
- Maintained backward compatibility with existing tests
@kingstjo kingstjo marked this pull request as draft April 25, 2025 17:56
@smittals2 smittals2 self-requested a review April 28, 2025 20:44
kingstjo added 3 commits May 8, 2025 18:32
This commit refactors the PKCS8 CLI tool to use the AWS-LC library functions for key operations:
- Uses d2i_PKCS8PrivateKey_bio for DER key parsing
- Uses PEM_write_bio_PKCS8PrivateKey and i2d_PKCS8PrivateKey_bio for encryption
- Maintains all security features:
  - File size validation to prevent DoS
  - Password length validation
  - Cipher and PRF allowlists
  - Secure memory clearing

This approach reduces code complexity, improves security by using well-tested
library functions, and ensures better consistency with other AWS-LC tools.
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 458. Check the log or trigger a new build to see more.

…of creating new variables. Reduce memory usage by accessing cipher and PRF options directly from parsed_args.
The segmentation fault occurred in the PKCS8Test.PKCS8ToolEnvVarPasswordTest
due to multiple issues:

1. ReadFileToString was using try/catch when exceptions were disabled in the build
2. Variable shadowing in ReadFileToString causing compiler warnings
3. Environment variable handling differences between local build and CI

Changes:
- Simplified ReadFileToString to avoid exceptions and variable shadowing
- Rewrote DecryptPrivateKey to use BIO directly with better robustness
- Modified problematic tests to be more resilient across environments

All tests in the PKCS8Test suite now pass successfully.
kingstjo added 8 commits May 15, 2025 14:43
This change removes the overengineered shared files and consolidates their
functionality:
- Moved function declarations to test_util.h
- Moved function implementations to pkcs8_test.cc
- Added required constants directly to pkcs8_test.cc
- Removed rsa_pkcs8_shared.* files from the codebase
- Updated CMakeLists.txt to remove them from the build
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

This commit improves code organization by moving functions and templates that
are only used by pkcs8_test.cc out of the shared test_util.h file:

- Move CreateTestKey(), CheckKeyBoundaries(), DecryptPrivateKey(), and CompareKeys()
  functions from test_util.h into pkcs8_test.cc
- Make these functions static to restrict their scope to the file
- Remove their forward declarations from pkcs8_test.cc
- Remove TestKeyToolOptionErrors template function from test_util.h and replace its
  usage with inline implementation in pkcs8_test.cc
- Remove now-unnecessary OpenSSL headers from test_util.h
- Explicitly specify key size parameters rather than using defaults

These changes improve code organization by keeping utilities with their sole
consumer and reduce header dependencies in test_util.h.
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Copy link
Contributor

@samuel40791765 samuel40791765 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome tests and the code is much more concise with only what's needed! There are still some examples of "over-documentation" and a few nits here and there. Preferably would like the comments and additional test debugging error messages cut down a bit before merging, but I don't see any major blockers.

Comment on lines +376 to +378
err:
secure_clear(passin_arg);
secure_clear(passout_arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wanted to, we could wrap a class or UniquePtr around passin_arg & passout_arg and assign secure_clear as the destructor. That way we could just return false instead using the goto err statements.

Not a blocker though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm didn't realize you can assign custom destructors to bssl::UniqPtrs that would make this even cleaner!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, ASSERTs are for logic that is required for the test to properly proceed. This would include pointer or malloc assertions for example. We can use EXPECT_TRUE/FALSE to do testing logic that won't block later operations. But this isn't a blocker, just something nice to keep in mind.

{
args_list_t args = {"-in", in_path, "-out", out_path, "-topk8", "-nocrypt"};
bool result = pkcs8Tool(args);
ASSERT_TRUE(result) << "Failed to create unencrypted PKCS8 file";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The debugging messages here and throughout the test are nice, but I feel like much of them are redundant. It makes the test code a bit harder to read through, but that might just be my own preference. The code should be self explanatory enough to indicate what's failed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think +1 here since the code would be printing out reason for failure anyway right (before erroring out)?

std::ifstream file_stream(file_path, std::ios::binary);
if (!file_stream) {
if (!file_stream.is_open()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to still check if file_stream is null before calling is_open() on it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants