diff --git a/changes/bug33032 b/changes/bug33032
new file mode 100644
index 0000000000000000000000000000000000000000..0c665f25dfacc6fb54722fac302ecd490cc0ca35
--- /dev/null
+++ b/changes/bug33032
@@ -0,0 +1,6 @@
+  o Minor bugfixes (key portability):
+    - When reading PEM-encoded key data, tolerate CRLF line-endings even if
+      we are not running on Windows. Previously, non-Windows hosts
+      would reject these line-endings in certain positions, making
+      certain key files hard to move from one host to another.
+      Fixes bug 33032; bugfix on 0.3.5.1-alpha.
diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c
index c48f1016ae94d3b7f0880016fb6e63305975e694..6c9f10e0851695068d931b5c06089db13a756b38 100644
--- a/src/lib/encoding/pem.c
+++ b/src/lib/encoding/pem.c
@@ -85,13 +85,19 @@ pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
   src = eat_whitespace_eos(src, eos);
 
   char *tag = NULL;
-  tor_asprintf(&tag, "-----BEGIN %s-----\n", objtype);
+  tor_asprintf(&tag, "-----BEGIN %s-----", objtype);
   if ((size_t)(eos-src) < strlen(tag) || fast_memneq(src, tag, strlen(tag))) {
     tor_free(tag);
     return -1;
   }
   src += strlen(tag);
   tor_free(tag);
+  /* At this point we insist on spaces (including CR), then an LF. */
+  src = eat_whitespace_eos_no_nl(src, eos);
+  if (src == eos || *src != '\n') {
+    /* Extra junk at end of line: this isn't valid. */
+    return -1;
+  }
 
   // NOTE lack of trailing \n.  We do not enforce its presence.
   tor_asprintf(&tag, "\n-----END %s-----", objtype);
diff --git a/src/test/test_pem.c b/src/test/test_pem.c
index 8f9f10f787681fd44e21c8b1c3cc4477343afac9..9772be124b3bf413bc2eaec0f3c014c527ec927b 100644
--- a/src/test/test_pem.c
+++ b/src/test/test_pem.c
@@ -115,8 +115,38 @@ test_crypto_pem_decode(void *arg)
   ;
 }
 
+static void
+test_crypto_pem_decode_crlf(void *arg)
+{
+  (void)arg;
+  char crlf_version[4096];
+  uint8_t buf[4096];
+
+  /* Convert 'expected' to a version with CRLF instead of LF. */
+  const char *inp = expected;
+  char *outp = crlf_version;
+  while (*inp) {
+    if (*inp == '\n') {
+      *outp++ = '\r';
+    }
+    *outp++ = *inp++;
+  }
+  *outp = 0;
+
+  /* Decoding should succeed (or else we have bug 33032 again) */
+  int n = pem_decode(buf, sizeof(buf),
+                     crlf_version, strlen(crlf_version),
+                     "WOMBAT QUOTE");
+  tt_int_op(n, OP_EQ, strlen(example_pre));
+  tt_mem_op(buf, OP_EQ, example_pre, n);
+
+ done:
+  ;
+}
+
 struct testcase_t pem_tests[] = {
   { "encode", test_crypto_pem_encode, 0, NULL, NULL },
   { "decode", test_crypto_pem_decode, 0, NULL, NULL },
+  { "decode_crlf", test_crypto_pem_decode_crlf, 0, NULL, NULL },
   END_OF_TESTCASES
 };