diff --git a/ChangeLog b/ChangeLog
index c279c9b17bf9ce13cbc52744a669d3b1987a3e91..1bd75ae9d3cec29b124fd1050146539947a289d7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 Changes in version 0.2.2.1-alpha - 2009-??-??
-  o Minor features
+  o Major features:
+    - Add support for dynamic OpenSSL hardware crypto acceleration engines
+      via new AccelName and AccelDir options.
+
+  o Minor features:
     - New --digests command-line switch to output the digests of the
       source files Tor was built with.
     - The "torify" script now uses torsocks where available.
diff --git a/doc/tor.1.in b/doc/tor.1.in
index d85747958bc8a3863b3917d444d334bb1818647b..3ac0f92fe23fa8d28b3fd0c391ca87ad0a8f1611 100644
--- a/doc/tor.1.in
+++ b/doc/tor.1.in
@@ -350,8 +350,19 @@ On startup, setuid to this user and setgid to their primary group.
 .LP
 .TP
 \fBHardwareAccel \fR\fB0\fR|\fB1\fP
-If non-zero, try to use crypto hardware acceleration when
-available. This is untested and probably buggy. (Default: 0)
+If non-zero, try to use built-in (static) crypto hardware acceleration when
+available. (Default: 0)
+.LP
+.TP
+\fBAccelName \fR\fINAME\fP
+When using OpenSSL hardware crypto acceleration attempt to load the dynamic
+engine of this name. This must be used for any dynamic hardware engine. Names
+can be verified with the openssl engine command.
+.LP
+.TP
+\fBAccelDir \fR\fIDIR\fP
+Specify this option if using dynamic hardware acceleration and the engine
+implementation library resides somewhere other than the OpenSSL default.
 .LP
 .TP
 \fBAvoidDiskWrites \fR\fB0\fR|\fB1\fP
diff --git a/src/common/crypto.c b/src/common/crypto.c
index da38ddc62e7291e2f46839a0b39166bb4ed2d365..57c636db68e517c90f1822788f136370e83bfdf5 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -27,6 +27,7 @@
 #include <openssl/rsa.h>
 #include <openssl/pem.h>
 #include <openssl/evp.h>
+#include <openssl/engine.h>
 #include <openssl/rand.h>
 #include <openssl/opensslv.h>
 #include <openssl/bn.h>
@@ -166,36 +167,70 @@ log_engine(const char *fn, ENGINE *e)
   }
 }
 
+/** Try to load an engine in a shared library via fully qualified path.
+ */
+static ENGINE *
+try_load_engine(const char *path, const char *engine)
+{
+  ENGINE *e = ENGINE_by_id("dynamic");
+  if (e) {
+    if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) ||
+        !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) ||
+        !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) ||
+        !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
+      ENGINE_free(e);
+      e = NULL;
+    }
+  }
+  return e;
+}
+
 /** Initialize the crypto library.  Return 0 on success, -1 on failure.
  */
 int
-crypto_global_init(int useAccel)
+crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
 {
   if (!_crypto_global_initialized) {
     ERR_load_crypto_strings();
     OpenSSL_add_all_algorithms();
     _crypto_global_initialized = 1;
     setup_openssl_threading();
-    /* XXX the below is a bug, since we can't know if we're supposed
-     * to be using hardware acceleration or not. we should arrange
-     * for this function to be called before init_keys. But make it
-     * not complain loudly, at least until we make acceleration work. */
-    if (useAccel < 0) {
-      log_info(LD_CRYPTO, "Initializing OpenSSL via tor_tls_init().");
-    }
     if (useAccel > 0) {
+      ENGINE *e = NULL;
       log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
       ENGINE_load_builtin_engines();
-      if (!ENGINE_register_all_complete())
-        return -1;
-
-      /* XXXX make sure this isn't leaking. */
+      ENGINE_register_all_complete();
+      if (accelName) {
+        if (accelDir) {
+          log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\""
+                   " via path \"%s\".", accelName, accelDir);
+          e = try_load_engine(accelName, accelDir);
+        } else {
+          log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\""
+                   " acceleration support.", accelName);
+          e = ENGINE_by_id(accelName);
+        }
+        if (!e) {
+          log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".",
+                   accelName);
+        } else {
+          log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".",
+                   accelName);
+        }
+      }
+      if (e) {
+        log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine,"
+                 " setting default ciphers.");
+        ENGINE_set_default(e, ENGINE_METHOD_ALL);
+      }
       log_engine("RSA", ENGINE_get_default_RSA());
       log_engine("DH", ENGINE_get_default_DH());
       log_engine("RAND", ENGINE_get_default_RAND());
       log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
       log_engine("3DES", ENGINE_get_cipher_engine(NID_des_ede3_ecb));
       log_engine("AES", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+    } else {
+      log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
     }
     return crypto_seed_rng(1);
   }
diff --git a/src/common/crypto.h b/src/common/crypto.h
index dd353ef0300b2b12b9b0e9624bbc4c016f93cb48..fa6735d78810683a3ce1f23aae36be07ff191b1e 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -55,7 +55,9 @@ typedef struct crypto_digest_env_t crypto_digest_env_t;
 typedef struct crypto_dh_env_t crypto_dh_env_t;
 
 /* global state */
-int crypto_global_init(int hardwareAccel);
+int crypto_global_init(int hardwareAccel,
+                       const char *accelName,
+                       const char *accelPath);
 void crypto_thread_cleanup(void);
 int crypto_global_cleanup(void);
 
diff --git a/src/common/tortls.c b/src/common/tortls.c
index f14eab18a51b22de9cf77840d6564b404f7b5c54..a518e83d203608af67af004b4f9ff83578752b7d 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -308,7 +308,6 @@ tor_tls_init(void)
   if (!tls_library_is_initialized) {
     SSL_library_init();
     SSL_load_error_strings();
-    crypto_global_init(-1);
     tls_library_is_initialized = 1;
   }
 }
diff --git a/src/or/config.c b/src/or/config.c
index c0f473bf2d1ffffe181580373e87c20b31f79177..daf50a1e2b23a76272a6ecf40c3923b1eabcf4f7 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -222,6 +222,8 @@ static config_var_t _option_vars[] = {
 #endif
   OBSOLETE("Group"),
   V(HardwareAccel,               BOOL,     "0"),
+  V(AccelName,                   STRING,   NULL),
+  V(AccelDir,                    FILENAME, NULL),
   V(HashedControlPassword,       LINELIST, NULL),
   V(HidServDirectoryV2,          BOOL,     "1"),
   VAR("HiddenServiceDir",    LINELIST_S, RendConfigLines,    NULL),
@@ -444,6 +446,10 @@ static config_var_description_t options_description[] = {
    * FetchUselessDescriptors */
   { "HardwareAccel", "If set, Tor tries to use hardware crypto accelerators "
     "when it can." },
+  { "AccelName", "If set, try to use hardware crypto accelerator with this "
+    "specific ID." },
+  { "AccelDir", "If set, look in this directory for the dynamic hardware "
+    "engine in addition to OpenSSL default path." },
   /* HashedControlPassword */
   { "HTTPProxy", "Force Tor to make all HTTP directory requests through this "
     "host:port (or host:80 if port is not set)." },
@@ -3611,6 +3617,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
                         "testing Tor network!");
   }
 
+  if (options->AccelName && !options->HardwareAccel)
+    options->HardwareAccel = 1;
+  if (options->AccelDir && !options->AccelName)
+    REJECT("Can't use hardware crypto accelerator dir without engine name.");
+
   return 0;
 #undef REJECT
 #undef COMPLAIN
@@ -3668,9 +3679,11 @@ options_transition_allowed(or_options_t *old, or_options_t *new_val,
     return -1;
   }
 
-  if (old->HardwareAccel != new_val->HardwareAccel) {
-    *msg = tor_strdup("While Tor is running, changing HardwareAccel is "
-                      "not allowed.");
+  if ((old->HardwareAccel != new_val->HardwareAccel)
+      || !opt_streq(old->AccelName, new_val->AccelName)
+      || !opt_streq(old->AccelDir, new_val->AccelDir)) {
+    *msg = tor_strdup("While Tor is running, changing OpenSSL hardware "
+                      "acceleration engine is not allowed.");
     return -1;
   }
 
diff --git a/src/or/main.c b/src/or/main.c
index b21c00c9c7666e6b7bda93b2a599ade264cde6d3..25cb949a0c8fd1bda9dfee8c92367543485c676f 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1810,7 +1810,9 @@ tor_init(int argc, char *argv[])
              "and you probably shouldn't.");
 #endif
 
-  if (crypto_global_init(get_options()->HardwareAccel)) {
+  if (crypto_global_init(get_options()->HardwareAccel,
+                         get_options()->AccelName,
+                         get_options()->AccelDir)) {
     log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
     return -1;
   }
diff --git a/src/or/or.h b/src/or/or.h
index 0d906e08702480db5ec2e96f259050ff55dc0ba6..5f91cec89f8ad0c55df34ab8438d8fc699548c8f 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2416,6 +2416,8 @@ typedef struct {
                   * log whether it was DNS-leaking or not? */
   int HardwareAccel; /**< Boolean: Should we enable OpenSSL hardware
                       * acceleration where available? */
+  char *AccelName; /**< Optional hardware acceleration engine name. */
+  char *AccelDir; /**< Optional hardware acceleration engine search dir. */
   int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number
                        * of fixed nodes? */
   int NumEntryGuards; /**< How many entry guards do we try to establish? */
diff --git a/src/or/router.c b/src/or/router.c
index a1a56b0d01f2b9d032820c29590463e1c9143443..e5f3b52ec935878617b6d70adbb980692318182b 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -442,7 +442,9 @@ init_keys(void)
     key_lock = tor_mutex_new();
 
   /* There are a couple of paths that put us here before */
-  if (crypto_global_init(get_options()->HardwareAccel)) {
+  if (crypto_global_init(get_options()->HardwareAccel,
+                         get_options()->AccelName,
+                         get_options()->AccelDir)) {
     log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
     return -1;
   }
diff --git a/src/or/test.c b/src/or/test.c
index d43eb8f225ea55e513dbbb7116a4dd69df62eb88..b2a70eadb38fcaef66236b57cfe62b8b4e4a78ed 100644
--- a/src/or/test.c
+++ b/src/or/test.c
@@ -4793,7 +4793,7 @@ main(int c, char**v)
   }
 
   options->command = CMD_RUN_UNITTESTS;
-  crypto_global_init(0);
+  crypto_global_init(0, NULL, NULL);
   rep_hist_init();
   network_init();
   setup_directory();
diff --git a/src/tools/tor-checkkey.c b/src/tools/tor-checkkey.c
index b29b52d8db05bdc0dfdb2653c2f296dfc69307bd..6416dbfbb39bb68d62bfe08ce4bb4a6b8fd8f8ca 100644
--- a/src/tools/tor-checkkey.c
+++ b/src/tools/tor-checkkey.c
@@ -29,7 +29,7 @@ int main(int c, char **v)
     return 1;
   }
 
-  if (crypto_global_init(0)) {
+  if (crypto_global_init(0, NULL, NULL)) {
     fprintf(stderr, "Couldn't initialize crypto library.\n");
     return 1;
   }
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index 4971668c9f513a3a4383aa93884d3cf11abbd3f5..d2ea4eb109f710a3f3d819a1f27de1e9244aa1a4 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -496,7 +496,7 @@ main(int argc, char **argv)
   init_logging();
 
   /* Don't bother using acceleration. */
-  if (crypto_global_init(0)) {
+  if (crypto_global_init(0, NULL, NULL)) {
     fprintf(stderr, "Couldn't initialize crypto library.\n");
     return 1;
   }