如何为X509_req对象设置颁发者名称?C++签名CSR咨询
Great question! Let's clear up the confusion first, then walk through a straightforward C++ implementation using OpenSSL.
First off, you’re right that x509_set_issuer_name() only works with X509 objects—but here’s the key insight: you don’t need to set an issuer name on the X509_REQ (CSR) itself.
A CSR is a request from a certificate applicant; it only contains the applicant’s subject details, public key, and their own signature. The issuer name is a field that belongs to the final signed X509 certificate, not the CSR. When you sign the CSR with your CA’s private key, you’ll create a new X509 object and set the issuer name there using your CA’s subject name.
Below is a practical, clean approach using OpenSSL (with RAII to avoid memory leaks and simplify resource management). This assumes you’re using OpenSSL 1.1.1 or later (adjustments for 3.0 are noted at the end).
Step-by-Step Explanation + Code
We’ll use smart pointers with custom deleters to handle OpenSSL objects safely, then walk through loading the CA assets, processing the CSR, and generating the signed certificate.
#include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/evp.h> #include <openssl/rand.h> #include <stdexcept> #include <memory> #include <cstdio> // Custom deleters for RAII management of OpenSSL objects struct X509Deleter { void operator()(X509* p) { if (p) X509_free(p); } }; struct X509ReqDeleter { void operator()(X509_REQ* p) { if (p) X509_REQ_free(p); } }; struct EVP_PKEYDeleter { void operator()(EVP_PKEY* p) { if (p) EVP_PKEY_free(p); } }; struct ASN1_INTEGERDeleter { void operator()(ASN1_INTEGER* p) { if (p) ASN1_INTEGER_free(p); } }; using X509Ptr = std::unique_ptr<X509, X509Deleter>; using X509ReqPtr = std::unique_ptr<X509_REQ, X509ReqDeleter>; using EVP_PKEYPtr = std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter>; using ASN1_INTEGERPtr = std::unique_ptr<ASN1_INTEGER, ASN1_INTEGERDeleter>; // Helper to generate a cryptographically secure serial number ASN1_INTEGERPtr generate_serial_number() { ASN1_INTEGERPtr serial(ASN1_INTEGER_new()); if (!serial) throw std::runtime_error("Failed to create serial number"); // Generate 16 bytes of random data for the serial (adjust size as needed) unsigned char buf[16]; if (RAND_bytes(buf, sizeof(buf)) != 1) { throw std::runtime_error("Failed to generate random serial number"); } if (!ASN1_INTEGER_set(serial.get(), (const BIGNUM*)buf)) { throw std::runtime_error("Failed to set serial number value"); } return serial; } X509Ptr sign_csr(const std::string& csr_path, const std::string& ca_cert_path, const std::string& ca_key_path, const std::string& output_cert_path) { // Load CA certificate FILE* ca_cert_fp = fopen(ca_cert_path.c_str(), "r"); if (!ca_cert_fp) throw std::runtime_error("Could not open CA certificate file"); X509Ptr ca_cert(PEM_read_X509(ca_cert_fp, nullptr, nullptr, nullptr)); fclose(ca_cert_fp); if (!ca_cert) throw std::runtime_error("Failed to load CA certificate"); // Load CA private key (add password argument if key is encrypted) FILE* ca_key_fp = fopen(ca_key_path.c_str(), "r"); if (!ca_key_fp) throw std::runtime_error("Could not open CA private key file"); EVP_PKEYPtr ca_key(PEM_read_PrivateKey(ca_key_fp, nullptr, nullptr, nullptr)); fclose(ca_key_fp); if (!ca_key) throw std::runtime_error("Failed to load CA private key"); // Load CSR FILE* csr_fp = fopen(csr_path.c_str(), "r"); if (!csr_fp) throw std::runtime_error("Could not open CSR file"); X509ReqPtr csr(PEM_read_X509_REQ(csr_fp, nullptr, nullptr, nullptr)); fclose(csr_fp); if (!csr) throw std::runtime_error("Failed to load CSR"); // Create new X509 certificate X509Ptr cert(X509_new()); if (!cert) throw std::runtime_error("Failed to create new X509 certificate"); // Set certificate version (X509v3 = version 2 in OpenSSL's indexing) if (!X509_set_version(cert.get(), 2)) { throw std::runtime_error("Failed to set certificate version"); } // Set secure serial number ASN1_INTEGERPtr serial = generate_serial_number(); X509_set_serialNumber(cert.get(), serial.get()); // Set validity period: start now, valid for 1 year X509_gmtime_adj(X509_get_notBefore(cert.get()), 0); X509_gmtime_adj(X509_get_notAfter(cert.get()), 365 * 24 * 3600); // Copy subject name from CSR to certificate if (!X509_set_subject_name(cert.get(), X509_REQ_get_subject_name(csr.get()))) { throw std::runtime_error("Failed to set certificate subject name"); } // Set issuer name from CA certificate if (!X509_set_issuer_name(cert.get(), X509_get_subject_name(ca_cert.get()))) { throw std::runtime_error("Failed to set certificate issuer name"); } // Extract public key from CSR and attach to certificate EVP_PKEYPtr pubkey(X509_REQ_get_pubkey(csr.get())); if (!pubkey) throw std::runtime_error("Failed to extract public key from CSR"); if (!X509_set_pubkey(cert.get(), pubkey.get())) { throw std::runtime_error("Failed to attach public key to certificate"); } // Sign the certificate with CA private key (using SHA-256 for security) if (!X509_sign(cert.get(), ca_key.get(), EVP_sha256())) { throw std::runtime_error("Failed to sign certificate with CA private key"); } // Save the signed certificate to file FILE* output_fp = fopen(output_cert_path.c_str(), "w"); if (!output_fp) throw std::runtime_error("Could not open output certificate file"); PEM_write_X509(output_fp, cert.get()); fclose(output_fp); return cert; } // Example usage int main() { try { auto signed_cert = sign_csr("my_request.csr", "ca_cert.crt", "ca_private.key", "signed_cert.crt"); printf("Successfully signed CSR! Output saved to signed_cert.crt\n"); } catch (const std::exception& e) { fprintf(stderr, "Error: %s\n", e.what()); // Optional: Print detailed OpenSSL errors ERR_print_errors_fp(stderr); return 1; } return 0; }
Key Notes for Production
- Serial Numbers: The example uses a random 16-byte serial number—this is critical for security (avoid sequential numbers that can be predicted).
- Encrypted CA Keys: If your CA private key is encrypted, pass a password string or password callback as the 4th argument to
PEM_read_PrivateKey(). - Hash Algorithms: Stick to SHA-256 or higher (SHA-1 is deprecated and insecure).
- OpenSSL 3.0 Compatibility: For OpenSSL 3.0, replace
X509_REQ_get_pubkey()withX509_REQ_get0_pubkey()(the latter returns a const pointer without incrementing the reference count). - Error Handling: Add
ERR_print_errors_fp(stderr)to debug OpenSSL-specific errors when things go wrong.
内容的提问来源于stack exchange,提问作者Shrikant Dhapke

