如何从SHA1哈希可靠生成OpenSSL认可的非负证书序列号?
Fixing Negative Serial Numbers in OpenSSL with SHA1 Hashes
Great question! This is a common pitfall when using raw hash values as X.509 certificate serial numbers—let's break down why it happens and how to fix it.
Why OpenSSL Sees Some Hashes as Negative
X.509 certificates require serial numbers to be positive integers, and OpenSSL follows the ASN.1 encoding standard for representing these integers. ASN.1 INTEGER types are signed, which means:
- If the first byte of the serial number has its highest bit (bit 7) set (i.e., the byte value is ≥
0x80), OpenSSL interprets the entire sequence as a negative number. - This violates the X.509 requirement, leading to errors or invalid certificates.
The Solution: Ensure Positive Integer Encoding
The fix is simple: adjust the SHA1 hash to comply with ASN.1's positive integer rules. Here's how to do it properly:
Step-by-Step Approach
- Generate your 20-byte SHA1 hash as usual (CA common name + 10 random bytes → SHA1).
- Check the first byte of the hash:
- If the byte is ≥
0x80(highest bit set), prepend a0x00byte to the hash. This leading zero tells ASN.1 the number is positive, resulting in a 21-byte serial number. - If the byte is <
0x80, use the raw 20-byte hash directly as the serial number.
- If the byte is ≥
Example Code (C with OpenSSL)
Here’s how you’d implement this in code when setting the certificate serial number:
#include <openssl/x509.h> #include <string.h> #include <stdlib.h> // Assume sha1_hash contains your 20-byte SHA1 result unsigned char sha1_hash[20]; // ... (generate the hash here) unsigned char *serial_data; int serial_length; // Check if we need to add a leading zero if (sha1_hash[0] & 0x80) { serial_length = 21; serial_data = malloc(serial_length); serial_data[0] = 0x00; memcpy(serial_data + 1, sha1_hash, 20); } else { serial_length = 20; serial_data = malloc(serial_length); memcpy(serial_data, sha1_hash, 20); } // Create the ASN.1 INTEGER and assign it to the certificate ASN1_INTEGER *serial = ASN1_INTEGER_new(); if (ASN1_INTEGER_set_octet_string(serial, serial_data, serial_length)) { X509_set_serialNumber(your_x509_cert, serial); } // Clean up allocated memory free(serial_data); ASN1_INTEGER_free(serial);
Key Notes
- This approach preserves the uniqueness of your SHA1-based serial number (adding a leading zero only when necessary, so no two distinct hashes will result in the same serial number).
- While many implementations prefer serial numbers ≤20 bytes for compatibility, adding one extra byte (to 21) is generally safe and won't cause issues with most systems.
- Never just flip the highest bit to 0—this would alter the hash value and risk collisions (two different inputs could produce the same serial number).
内容的提问来源于stack exchange,提问作者Timothy John Laird
相关产品推荐
相关产品推荐

