]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
stable certificates part2
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 21 Feb 2012 17:25:53 +0000 (19:25 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 21 Feb 2012 17:25:53 +0000 (19:25 +0200)
Two different certificates of the same fake Issuer must have the same serial
number. Otherwise, Firefox and possibly others will display a
sec_error_reused_issuer_and_serial error. Similarly, the same two certificates
should have the same serial number, even if generated on different
non-communicating (but identically configured) Squid boxes.

To produce unique serial numbers a temporary fake certificate with serial number
zero created, and its fingerprint used as the serial number of the final fake
certificate.

The old Ssl::CertificateDb code which was responsible to produce a serial number
for generated certificates removed.

src/ssl/certificate_db.cc
src/ssl/certificate_db.h
src/ssl/gadgets.cc
src/ssl/gadgets.h
src/ssl/ssl_crtd.cc

index 381d6235ffaef058f5caf1342ca0d75ab2a15755..88e70cceb5bb0b1acb746c3ef0a7c49b8c7dd61c 100644 (file)
@@ -171,7 +171,6 @@ int Ssl::CertificateDb::index_name_cmp(const char **a, const char **b)
     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");
@@ -179,7 +178,6 @@ const size_t Ssl::CertificateDb::min_db_size(4096);
 
 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),
@@ -187,7 +185,6 @@ Ssl::CertificateDb::CertificateDb(std::string const & aDb_path, size_t aMax_db_s
         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)
@@ -219,8 +216,11 @@ bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP
     }
     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));
@@ -261,56 +261,10 @@ bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP
     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);
@@ -329,18 +283,6 @@ void Ssl::CertificateDb::create(std::string const & db_path, int serial)
 #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;
@@ -357,17 +299,6 @@ void Ssl::CertificateDb::check(std::string const & db_path, size_t max_db_size)
     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)
index 05841b6f26064b96ef10b5a50013e93685b633ab..2a31d20b7e7f3d452c3c4e932ad56347aee4ac81 100644 (file)
@@ -96,13 +96,10 @@ public:
     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.
@@ -154,7 +151,6 @@ private:
     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.
@@ -162,7 +158,6 @@ private:
     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.
@@ -171,7 +166,6 @@ private:
     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.
 };
index fd7427e62b1dce29fa71a613f00c1feb7b0fceec..d44fc3c3c38744b830c2eb627e4240d00ea077d1 100644 (file)
@@ -183,7 +183,7 @@ const char *Ssl::CertAdaptAlgorithmStr[] = {
     NULL
 };
 
-Ssl::CertificateProperties::CertificateProperties(): serial(NULL),
+Ssl::CertificateProperties::CertificateProperties():
                                                      setValidAfter(false),
                                                      setValidBefore(false),
                                                      setCommonName(false),
@@ -289,14 +289,14 @@ static bool buildCertificate(Ssl::X509_Pointer & cert, Ssl::CertificatePropertie
     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;
@@ -308,7 +308,7 @@ bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_
     // 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
@@ -338,6 +338,68 @@ bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_
     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.
index f041796b6c5ff5b3ef7c615cb10a28a7710a16de..1d099ea6a80c4d8fa26b87b7f458ff7e2ecb31a5 100644 (file)
@@ -204,7 +204,6 @@ public:
     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
index c2bec535e2f16f01ea9efe24ac0f2758ad1efe88..9ef7a2dba83b829fdd78bce1ad00bfa805e8ece3 100644 (file)
@@ -72,14 +72,9 @@ usage: ssl_crtd -hv -s ssl_storage_path -M storage_max_size
         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
  */
 
@@ -195,13 +190,8 @@ static void usage()
         "-----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;
 }
 
@@ -233,8 +223,6 @@ static bool proccessNewRequest(Ssl::CrtdMessage & request_message, std::string c
     }
 
     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.");
 
@@ -263,12 +251,10 @@ static bool proccessNewRequest(Ssl::CrtdMessage & request_message, std::string c
 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) {
@@ -284,11 +270,6 @@ int main(int argc, char *argv[])
             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");
@@ -301,9 +282,6 @@ int main(int argc, char *argv[])
             case 'c':
                 create_new_db = true;
                 break;
-            case 'g':
-                show_sn = true;
-                break;
             case 'h':
                 usage();
                 exit(0);
@@ -314,16 +292,11 @@ int main(int argc, char *argv[])
 
         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);
         }