return(strcmp(a[Ssl::CertificateDb::cnlName], b[CertificateDb::cnlName]));
}
-const std::string Ssl::CertificateDb::serial_file("serial");
const std::string Ssl::CertificateDb::db_file("index.txt");
const std::string Ssl::CertificateDb::cert_dir("certs");
const std::string Ssl::CertificateDb::size_file("size");
Ssl::CertificateDb::CertificateDb(std::string const & aDb_path, size_t aMax_db_size, size_t aFs_block_size)
: db_path(aDb_path),
- serial_full(aDb_path + "/" + serial_file),
db_full(aDb_path + "/" + db_file),
cert_full(aDb_path + "/" + cert_dir),
size_full(aDb_path + "/" + size_file),
max_db_size(aMax_db_size),
fs_block_size(aFs_block_size),
dbLock(db_full),
- dbSerialLock(serial_full),
enabled_disk_store(true)
{
if (db_path.empty() && !max_db_size)
}
row.setValue(cnlSerial, serial_string.c_str());
char ** rrow = TXT_DB_get_by_index(db.get(), cnlSerial, row.getRow());
+ // We are creating certificates with unique serial number. If the serial
+ // number found in the database, means that the certificate already exist
+ // in the database
if (rrow != NULL)
- return false;
+ return true;
{
TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0));
return true;
}
-BIGNUM * Ssl::CertificateDb::getCurrentSerialNumber()
-{
- const Locker locker(dbSerialLock, Here);
- // load serial number from file.
- Ssl::BIO_Pointer file(BIO_new(BIO_s_file()));
- if (!file)
- return NULL;
-
- if (BIO_rw_filename(file.get(), const_cast<char *>(serial_full.c_str())) <= 0)
- return NULL;
-
- Ssl::ASN1_INT_Pointer serial_ai(ASN1_INTEGER_new());
- if (!serial_ai)
- return NULL;
-
- char buffer[1024];
- if (!a2i_ASN1_INTEGER(file.get(), serial_ai.get(), buffer, sizeof(buffer)))
- return NULL;
-
- Ssl::BIGNUM_Pointer serial(ASN1_INTEGER_to_BN(serial_ai.get(), NULL));
-
- if (!serial)
- return NULL;
-
- // increase serial number.
- Ssl::BIGNUM_Pointer increased_serial(BN_dup(serial.get()));
- if (!increased_serial)
- return NULL;
-
- BN_add_word(increased_serial.get(), 1);
-
- // save increased serial number.
- if (BIO_seek(file.get(), 0))
- return NULL;
-
- Ssl::ASN1_INT_Pointer increased_serial_ai(BN_to_ASN1_INTEGER(increased_serial.get(), NULL));
- if (!increased_serial_ai)
- return NULL;
-
- i2a_ASN1_INTEGER(file.get(), increased_serial_ai.get());
- BIO_puts(file.get(),"\n");
-
- return serial.release();
-}
-
-void Ssl::CertificateDb::create(std::string const & db_path, int serial)
+void Ssl::CertificateDb::create(std::string const & db_path)
{
if (db_path == "")
throw std::runtime_error("Path to db is empty");
- std::string serial_full(db_path + "/" + serial_file);
std::string db_full(db_path + "/" + db_file);
std::string cert_full(db_path + "/" + cert_dir);
std::string size_full(db_path + "/" + size_file);
#endif
throw std::runtime_error("Cannot create " + cert_full);
- Ssl::ASN1_INT_Pointer i(ASN1_INTEGER_new());
- ASN1_INTEGER_set(i.get(), serial);
-
- Ssl::BIO_Pointer file(BIO_new(BIO_s_file()));
- if (!file)
- throw std::runtime_error("SSL error");
-
- if (BIO_write_filename(file.get(), const_cast<char *>(serial_full.c_str())) <= 0)
- throw std::runtime_error("Cannot open " + cert_full + " to open");
-
- i2a_ASN1_INTEGER(file.get(), i.get());
-
std::ofstream size(size_full.c_str());
if (size)
size << 0;
db.load();
}
-std::string Ssl::CertificateDb::getSNString() const
-{
- const Locker locker(dbSerialLock, Here);
- std::ifstream file(serial_full.c_str());
- if (!file)
- return "";
- std::string serial;
- file >> serial;
- return serial;
-}
-
bool Ssl::CertificateDb::pure_find(std::string const & host_name, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
{
if (!db)
bool find(std::string const & host_name, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
/// Save certificate to disk.
bool addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, std::string const & useName);
- /// Get a serial number to use for generating a new certificate.
- BIGNUM * getCurrentSerialNumber();
/// Create and initialize a database under the db_path
- static void create(std::string const & db_path, int serial);
+ static void create(std::string const & db_path);
/// Check the database stored under the db_path.
static void check(std::string const & db_path, size_t max_db_size);
- std::string getSNString() const; ///< Get serial number as string.
bool IsEnabledDiskStore() const; ///< Check enabled of dist store.
private:
void load(); ///< Load db from disk.
static IMPLEMENT_LHASH_COMP_FN(index_name_cmp,const char **)
#endif
- static const std::string serial_file; ///< Base name of the file to store serial number.
static const std::string db_file; ///< Base name of the database index file.
static const std::string cert_dir; ///< Base name of the directory to store the certs.
static const std::string size_file; ///< Base name of the file to store db size.
static const size_t min_db_size;
const std::string db_path; ///< The database directory.
- const std::string serial_full; ///< Full path of the file to store serial number.
const std::string db_full; ///< Full path of the database index file.
const std::string cert_full; ///< Full path of the directory to store the certs.
const std::string size_full; ///< Full path of the file to store the db size.
const size_t max_db_size; ///< Max size of db.
const size_t fs_block_size; ///< File system block size.
mutable Lock dbLock; ///< protects the database file
- mutable Lock dbSerialLock; ///< protects the serial number file
bool enabled_disk_store; ///< The storage on the disk is enabled.
};
NULL
};
-Ssl::CertificateProperties::CertificateProperties(): serial(NULL),
+Ssl::CertificateProperties::CertificateProperties():
setValidAfter(false),
setValidBefore(false),
setCommonName(false),
return true;
}
-bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties)
+static bool generateFakeSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial)
{
Ssl::EVP_PKEY_Pointer pkey;
// Use signing certificates private key as generated certificate private key
if (properties.signWithPkey.get())
pkey.resetAndLock(properties.signWithPkey.get());
else // if not exist generate one
- pkey.reset(createSslPrivateKey());
+ pkey.reset(Ssl::createSslPrivateKey());
if (!pkey)
return false;
// Set pub key and serial given by the caller
if (!X509_set_pubkey(cert.get(), pkey.get()))
return false;
- if (!setSerialNumber(X509_get_serialNumber(cert.get()), properties.serial.get()))
+ if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial.get()))
return false;
// Fill the certificate with the required properties
return true;
}
+/// Return the SHA1 digest of the DER encoded version of the certificate
+/// stored in a BIGNUM
+static BIGNUM *x509Fingerprint(Ssl::X509_Pointer const & cert)
+{
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
+
+ if (!X509_digest(cert.get(),EVP_sha1(),md,&n))
+ return NULL;
+
+ assert(n == 20); //for sha1 n is 20 (for md5 n is 16)
+
+ BIGNUM *r = NULL;
+ r = BN_bin2bn(md, n, NULL);
+ return r;
+}
+
+/// Generate a unique serial number based on a Ssl::CertificateProperties object
+/// for a new generated certificate
+static bool createSerial(Ssl::BIGNUM_Pointer &serial, Ssl::CertificateProperties const &properties)
+{
+ Ssl::EVP_PKEY_Pointer fakePkey;
+ Ssl::X509_Pointer fakeCert;
+
+ serial.reset(BN_new());
+ BN_zero(serial.get());
+ if (!generateFakeSslCertificate(fakeCert, fakePkey, properties, serial))
+ return false;
+
+ // The x509Fingerprint return an SHA1 hash.
+ // both SHA1 hash and maximum serial number size are 20 bytes.
+ BIGNUM *r = x509Fingerprint(fakeCert);
+ if (!r)
+ return false;
+
+ // if the serial is "0" set it to '1'
+ if (BN_is_zero(r))
+ BN_one(r);
+
+ // According the RFC 5280, serial is an 20 bytes ASN.1 INTEGER (a signed big integer)
+ // and the maximum value for X.509 certificate serial number is 2^159-1 and
+ // the minimum 0. If the first bit of the serial is '1' ( eg 2^160-1),
+ // will result to a negative integer.
+ // To handle this, if the produced serial is greater than 2^159-1
+ // truncate the last bit
+ if (BN_is_bit_set(r, 159))
+ BN_clear_bit(r, 159);
+
+ serial.reset(r);
+ return true;
+}
+
+bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties)
+{
+ Ssl::BIGNUM_Pointer serial;
+
+ if (!createSerial(serial, properties))
+ return false;
+
+ return generateFakeSslCertificate(certToStore, pkeyToStore, properties, serial);
+}
+
/**
\ingroup ServerProtocolSSLInternal
* Read certificate from file.
X509_Pointer mimicCert; ///< Certificate to mimic
X509_Pointer signWithX509; ///< Certificate to sign the generated request
EVP_PKEY_Pointer signWithPkey; ///< The key of the signing certificate
- BIGNUM_Pointer serial; ///< Use this serial for generated certificate
bool setValidAfter; ///< Do not mimic "Not Valid After" field
bool setValidBefore; ///< Do not mimic "Not Valid Before" field
bool setCommonName; ///< Replace the CN field of the mimicing subject with the given
Create new private key and certificate request for "host.dom".
Sign new request by received certificate and private key.
-usage: ssl_crtd -c -s ssl_store_path\n -n new_serial_number
+usage: ssl_crtd -c -s ssl_store_path\n
-c Init ssl db directories and exit.
- -n new_serial_number HEX serial number to use when initializing db.
- The default value of serial number is
- the number of seconds since Epoch minus 1200000000
-usage: ssl_crtd -g -s ssl_store_path
- -g Show current serial number and exit.
\endverbatim
*/
"-----END RSA PRIVATE KEY-----\n"
"\tCreate new private key and certificate request for \"host.dom\"\n"
"\tSign new request by received certificate and private key.\n"
- "usage: ssl_crtd -c -s ssl_store_path -n new_serial_number\n"
- "\t-c Init ssl db directories and exit.\n"
- "\t-n new_serial_number HEX serial number to use when initializing db.\n"
- "\t The default value of serial number is\n"
- "\t the number of seconds since Epoch minus 1200000000\n"
- "usage: ssl_crtd -g -s ssl_store_path\n"
- "\t-g Show current serial number and exit.";
+ "usage: ssl_crtd -c -s ssl_store_path\n"
+ "\t-c Init ssl db directories and exit.\n";
std::cerr << help_string << std::endl;
}
}
if (!cert || !pkey) {
- certProperties.serial.reset(db.getCurrentSerialNumber());
-
if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
throw std::runtime_error("Cannot create ssl certificate or private key.");
int main(int argc, char *argv[])
{
try {
- int serial = (getCurrentTime() - 1200000000);
size_t max_db_size = 0;
size_t fs_block_size = 2048;
char c;
bool create_new_db = false;
- bool show_sn = false;
std::string db_path;
// proccess options.
while ((c = getopt(argc, argv, "dcghvs:M:b:n:")) != -1) {
case 's':
db_path = optarg;
break;
- case 'n': {
- std::stringstream sn_stream(optarg);
- sn_stream >> std::hex >> serial;
- break;
- }
case 'M':
if (!parseBytesOptionValue(&max_db_size, optarg)) {
throw std::runtime_error("Error when parsing -M options value");
case 'c':
create_new_db = true;
break;
- case 'g':
- show_sn = true;
- break;
case 'h':
usage();
exit(0);
if (create_new_db) {
std::cout << "Initialization SSL db..." << std::endl;
- Ssl::CertificateDb::create(db_path, serial);
+ Ssl::CertificateDb::create(db_path);
std::cout << "Done" << std::endl;
exit(0);
}
- if (show_sn) {
- Ssl::CertificateDb db(db_path, 4096, 0);
- std::cout << db.getSNString() << std::endl;
- exit(0);
- }
{
Ssl::CertificateDb::check(db_path, max_db_size);
}