diff --git a/config.h.cmake b/config.h.cmake
index 7989cbfb..6f5e147e 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -24,9 +24,6 @@
 /* Use zlib instead of builtin zlib decoder to uncompress flate streams. */
 #cmakedefine ENABLE_ZLIB_UNCOMPRESS 1
 
-/* Build against libnss3 for digital signature validation */
-#cmakedefine ENABLE_NSS3 1
-
 /* Use cairo for rendering. */
 #cmakedefine HAVE_CAIRO 1
 
diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc
index 16476f4f..9f4adda3 100644
--- a/poppler/Decrypt.cc
+++ b/poppler/Decrypt.cc
@@ -39,17 +39,33 @@
 #include "goo/grandom.h"
 #include "Decrypt.h"
 #include "Error.h"
-
+#ifdef ENABLE_NSS3
+#include <nss.h>
+#include <prerror.h>
+#include <sechash.h>
+#endif
+
+#ifdef ENABLE_NSS3
+static PK11Context *rc4InitContext(const unsigned char *key, int keyLen);
+static unsigned char rc4DecryptByte(PK11Context *context, unsigned char c);
+#else
 static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state);
 static unsigned char rc4DecryptByte(unsigned char *state, unsigned char *x, unsigned char *y, unsigned char c);
+#endif
 
 static bool aesReadBlock(Stream *str, unsigned char *in, bool addPadding);
 
+#ifdef ENABLE_NSS3
+static PK11Context *aesInitContext(unsigned char *in, unsigned char *objKey, int objKeyLength, bool decrypt);
+#else
 static void aesKeyExpansion(DecryptAESState *s, const unsigned char *objKey, int objKeyLen, bool decrypt);
+#endif
 static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in);
 static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last);
 
+#ifndef ENABLE_NSS3
 static void aes256KeyExpansion(DecryptAES256State *s, const unsigned char *objKey, int objKeyLen, bool decrypt);
+#endif
 static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in);
 static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in, bool last);
 
@@ -70,6 +86,31 @@ static const unsigned char passwordPad[32] = {
 // Decrypt
 //------------------------------------------------------------------------
 
+#ifdef ENABLE_NSS3
+static void shutdownNSS()
+{
+    if (NSS_Shutdown() != SECSuccess) {
+        error(errInternal, -1, "NSS shutdown failed: {0:s}",
+              PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
+    }
+}
+
+static bool initNSS() {
+    if (NSS_IsInitialized()) {
+        return true;
+    } else {
+        if (NSS_NoDB_Init(".") != SECSuccess) {
+            error(errInternal, -1, "NSS initialization failed: {0:s}",
+                  PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
+            return false;
+        } else {
+            atexit(shutdownNSS);
+            return true;
+        }
+    }
+}
+#endif
+
 bool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, const GooString *ownerKey, const GooString *userKey, const GooString *ownerEnc, const GooString *userEnc, int permissions, const GooString *fileID,
                           const GooString *ownerPassword, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata, bool *ownerPasswordOk)
 {
@@ -80,13 +121,21 @@ bool Decrypt::makeFileKey(int encVersio
     DecryptAES256State state;
     unsigned char test[127 + 56], test2[32];
     GooString *userPassword2;
-    unsigned char fState[256];
     unsigned char tmpKey[16];
-    unsigned char fx, fy;
     int len, i, j;
+#ifdef ENABLE_NSS3
+    PK11Context *rc4Context;
+#else
+    unsigned char fState[256];
+    unsigned char fx, fy;
+#endif
 
     *ownerPasswordOk = false;
 
+#ifdef ENABLE_NSS3
+    initNSS();
+#endif
+
     if (encRevision == 5 || encRevision == 6) {
 
         // check the owner password
@@ -115,14 +164,26 @@ bool Decrypt::makeFileKey(int encVersio
                     // test contains the initial SHA-256 hash input K.
                     revision6Hash(ownerPassword, test, userKey->c_str());
                 }
+#ifndef ENABLE_NSS3
                 aes256KeyExpansion(&state, test, 32, true);
+#endif
                 for (i = 0; i < 16; ++i) {
                     state.cbc[i] = 0;
                 }
+#ifdef ENABLE_NSS3
+                state.context = aesInitContext(state.cbc, test, 32, true);
+                if (state.context) {
+#endif
                 aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str(), false);
                 memcpy(fileKey, state.buf, 16);
                 aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str() + 16, false);
                 memcpy(fileKey + 16, state.buf, 16);
+#ifdef ENABLE_NSS3
+                PK11_DestroyContext(state.context, PR_TRUE);
+                } else {
+                    return false;
+                }
+#endif
 
                 *ownerPasswordOk = true;
                 return true;
@@ -156,14 +217,26 @@ bool Decrypt::makeFileKey(int encVersio
                     // user key is not used in computing intermediate user key.
                     revision6Hash(userPassword, test, nullptr);
                 }
+#ifndef ENABLE_NSS3
                 aes256KeyExpansion(&state, test, 32, true);
+#endif
                 for (i = 0; i < 16; ++i) {
                     state.cbc[i] = 0;
                 }
+#ifdef ENABLE_NSS3
+                state.context = aesInitContext(state.cbc, test, 32, true);
+                if (state.context) {
+#endif
                 aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str(), false);
                 memcpy(fileKey, state.buf, 16);
                 aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str() + 16, false);
                 memcpy(fileKey + 16, state.buf, 16);
+#ifdef ENABLE_NSS3
+                PK11_DestroyContext(state.context, PR_TRUE);
+                } else {
+                    return false;
+                }
+#endif
 
                 return true;
             }
@@ -189,22 +262,41 @@ bool Decrypt::makeFileKey(int encVersio
                 }
             }
             if (encRevision == 2) {
+#ifdef ENABLE_NSS3
+                rc4Context = rc4InitContext(test, keyLength);
+                if (rc4Context) {
+                    for (i = 0; i < 32; ++i)
+                        test2[i] = rc4DecryptByte(rc4Context, ownerKey->getChar(i));
+                    PK11_DestroyContext(rc4Context, PR_TRUE);
+                }
+#else
                 rc4InitKey(test, keyLength, fState);
                 fx = fy = 0;
                 for (i = 0; i < 32; ++i) {
                     test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
                 }
+#endif
             } else {
                 memcpy(test2, ownerKey->c_str(), 32);
                 for (i = 19; i >= 0; --i) {
                     for (j = 0; j < keyLength; ++j) {
                         tmpKey[j] = test[j] ^ i;
                     }
+#ifdef ENABLE_NSS3
+                    rc4Context = rc4InitContext(tmpKey, keyLength);
+                    if (rc4Context) {
+                        for (j = 0; j < 32; ++j) {
+                            test2[j] = rc4DecryptByte(rc4Context, test2[j]);
+                        }
+                        PK11_DestroyContext(rc4Context, PR_TRUE);
+                    }
+#else
                     rc4InitKey(tmpKey, keyLength, fState);
                     fx = fy = 0;
                     for (j = 0; j < 32; ++j) {
                         test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
                     }
+#endif
                 }
             }
             userPassword2 = new GooString((char *)test2, 32);
@@ -232,11 +324,15 @@ bool Decrypt::makeFileKey2(int encVersi
 {
     unsigned char *buf;
     unsigned char test[32];
-    unsigned char fState[256];
     unsigned char tmpKey[16];
-    unsigned char fx, fy;
     int len, i, j;
-    bool ok;
+    bool ok = true;
+#ifdef ENABLE_NSS3
+    PK11Context *rc4Context;
+#else
+    unsigned char fState[256];
+    unsigned char fx, fy;
+#endif
 
     // generate file key
     buf = (unsigned char *)gmalloc(72 + fileID->getLength());
@@ -273,28 +369,52 @@ bool Decrypt::makeFileKey2(int encVersi
 
     // test user password
     if (encRevision == 2) {
+#ifdef ENABLE_NSS3
+        rc4Context = rc4InitContext(fileKey, keyLength);
+        if (rc4Context) {
+            for (i = 0; i < 32; ++i)
+                test[i] = rc4DecryptByte(rc4Context, userKey->getChar(i));
+            PK11_DestroyContext(rc4Context, PR_TRUE);
+        } else {
+            ok = false;
+        }
+#else
         rc4InitKey(fileKey, keyLength, fState);
         fx = fy = 0;
         for (i = 0; i < 32; ++i) {
             test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
         }
-        ok = memcmp(test, passwordPad, 32) == 0;
+#endif
+        if (ok)
+            ok = memcmp(test, passwordPad, 32) == 0;
     } else if (encRevision == 3) {
         memcpy(test, userKey->c_str(), 32);
         for (i = 19; i >= 0; --i) {
             for (j = 0; j < keyLength; ++j) {
                 tmpKey[j] = fileKey[j] ^ i;
             }
+#ifdef ENABLE_NSS3
+            rc4Context = rc4InitContext(tmpKey, keyLength);
+            if (rc4Context) {
+                for (j = 0; j < 32; ++j)
+                    test[j] = rc4DecryptByte(rc4Context, test[j]);
+                PK11_DestroyContext(rc4Context, PR_TRUE);
+            } else {
+                ok = false;
+            }
+#else
             rc4InitKey(tmpKey, keyLength, fState);
             fx = fy = 0;
             for (j = 0; j < 32; ++j) {
                 test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
             }
+#endif
         }
         memcpy(buf, passwordPad, 32);
         memcpy(buf + 32, fileID->c_str(), fileID->getLength());
         md5(buf, 32 + fileID->getLength(), buf);
-        ok = memcmp(test, buf, 16) == 0;
+        if (ok)
+            ok = memcmp(test, buf, 16) == 0;
     } else {
         ok = false;
     }
@@ -334,6 +454,9 @@ BaseCryptStream::BaseCryptStream(Stream
         if ((objKeyLength = keyLength + 5) > 16) {
             objKeyLength = 16;
         }
+#ifdef ENABLE_NSS3
+        state.rc4.context = nullptr;
+#endif
         break;
     case cryptAES:
         objKey[keyLength] = refA.num & 0xff;
@@ -349,9 +472,15 @@ BaseCryptStream::BaseCryptStream(Stream
         if ((objKeyLength = keyLength + 5) > 16) {
             objKeyLength = 16;
         }
+#ifdef ENABLE_NSS3
+        state.aes.context = nullptr;
+#endif
         break;
     case cryptAES256:
         objKeyLength = keyLength;
+#ifdef ENABLE_NSS3
+        state.aes256.context = nullptr;
+#endif
         break;
     case cryptNone:
         break;
@@ -359,10 +488,33 @@ BaseCryptStream::BaseCryptStream(Stream
     charactersRead = 0;
     nextCharBuff = EOF;
     autoDelete = true;
+
+#ifdef ENABLE_NSS3
+    initNSS();
+#endif
 }
 
 BaseCryptStream::~BaseCryptStream()
 {
+#ifdef ENABLE_NSS3
+    switch (algo) {
+    case cryptRC4:
+        if (state.rc4.context)
+            PK11_DestroyContext(state.rc4.context, PR_TRUE);
+        break;
+    case cryptAES:
+        if (state.aes.context)
+            PK11_DestroyContext(state.aes.context, PR_TRUE);
+        break;
+    case cryptAES256:
+        if (state.aes256.context)
+            PK11_DestroyContext(state.aes256.context, PR_TRUE);
+        break;
+    default:
+        break;
+    }
+#endif
+
     if (autoDelete) {
         delete str;
     }
@@ -424,18 +576,40 @@ void EncryptStream::reset() {
 
     switch (algo) {
     case cryptRC4:
+#ifdef ENABLE_NSS3
+        if (state.rc4.context)
+            PK11_DestroyContext(state.rc4.context, PR_TRUE);
+        state.rc4.context = rc4InitContext(objKey, objKeyLength);
+#else
         state.rc4.x = state.rc4.y = 0;
         rc4InitKey(objKey, objKeyLength, state.rc4.state);
+#endif
         break;
     case cryptAES:
+#ifdef ENABLE_NSS3
+        memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
+        if (state.aes.context)
+            PK11_DestroyContext(state.aes.context, PR_TRUE);
+        state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength,
+                                           false);
+#else
         aesKeyExpansion(&state.aes, objKey, objKeyLength, false);
         memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
+#endif
         state.aes.bufIdx = 0;
         state.aes.paddingReached = false;
         break;
     case cryptAES256:
+#ifdef ENABLE_NSS3
+        memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
+        if (state.aes256.context)
+            PK11_DestroyContext(state.aes256.context, PR_TRUE);
+        state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength,
+                                              false);
+#else
         aes256KeyExpansion(&state.aes256, objKey, objKeyLength, false);
         memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
+#endif
         state.aes256.bufIdx = 0;
         state.aes256.paddingReached = false;
         break;
@@ -456,7 +630,11 @@ int EncryptStream::lookChar() {
     case cryptRC4:
         if ((c = str->getChar()) != EOF) {
             // RC4 is XOR-based: the decryption algorithm works for encryption too
+#ifdef ENABLE_NSS3
+            c = rc4DecryptByte(state.rc4.context, (unsigned char)c);
+#else
             c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)c);
+#endif
         }
         break;
     case cryptAES:
@@ -506,21 +684,47 @@ void DecryptStream::reset() {
 
     switch (algo) {
     case cryptRC4:
+#ifdef ENABLE_NSS3
+        if (state.rc4.context)
+            PK11_DestroyContext(state.rc4.context, PR_TRUE);
+        state.rc4.context = rc4InitContext(objKey, objKeyLength);
+#else
         state.rc4.x = state.rc4.y = 0;
         rc4InitKey(objKey, objKeyLength, state.rc4.state);
+#endif
         break;
     case cryptAES:
+#ifdef ENABLE_NSS3
+        if (state.aes.context)
+            PK11_DestroyContext(state.aes.context, PR_TRUE);
+        for (i = 0; i < 16; ++i) {
+            state.aes.cbc[i] = str->getChar();
+        }
+        state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength,
+                                           true);
+#else
         aesKeyExpansion(&state.aes, objKey, objKeyLength, true);
         for (i = 0; i < 16; ++i) {
             state.aes.cbc[i] = str->getChar();
         }
+#endif
         state.aes.bufIdx = 16;
         break;
     case cryptAES256:
+#ifdef ENABLE_NSS3
+        if (state.aes256.context)
+            PK11_DestroyContext(state.aes256.context, PR_TRUE);
+        for (i = 0; i < 16; ++i) {
+            state.aes256.cbc[i] = str->getChar();
+        }
+        state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength,
+                                              true);
+#else
         aes256KeyExpansion(&state.aes256, objKey, objKeyLength, true);
         for (i = 0; i < 16; ++i) {
             state.aes256.cbc[i] = str->getChar();
         }
+#endif
         state.aes256.bufIdx = 16;
         break;
     case cryptNone:
@@ -539,10 +743,21 @@ int DecryptStream::lookChar() {
     switch (algo) {
     case cryptRC4:
         if ((c = str->getChar()) != EOF) {
+#ifdef ENABLE_NSS3
+            if (unlikely(state.rc4.context == nullptr))
+                c = EOF;
+            else
+                c = rc4DecryptByte(state.rc4.context, (unsigned char)c);
+#else
             c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)c);
+#endif
         }
         break;
     case cryptAES:
+#ifdef ENABLE_NSS3
+        if (unlikely(state.aes.context == nullptr))
+            break;
+#endif
         if (state.aes.bufIdx == 16) {
             if (aesReadBlock(str, in, false)) {
                 aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
@@ -555,6 +770,10 @@ int DecryptStream::lookChar() {
         }
         break;
     case cryptAES256:
+#ifdef ENABLE_NSS3
+        if (unlikely(state.aes256.context == nullptr))
+            break;
+#endif
         if (state.aes256.bufIdx == 16) {
             if (aesReadBlock(str, in, false)) {
                 aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
@@ -576,7 +795,176 @@ int DecryptStream::lookChar() {
 // RC4-compatible decryption
 //------------------------------------------------------------------------
 
+#ifdef ENABLE_NSS3
+/*
+ * This function turns given key into token key (compared to a session key
+ * which is prohibited in FIPS mode).
+ */
+static PK11SymKey *tokenizeKey(const unsigned char *key, int keyLen,
+                               CK_ATTRIBUTE_TYPE operation) {
+    CK_MECHANISM_TYPE  cipherMech = CKM_AES_CBC_PAD;
+    PK11SlotInfo      *slot;
+    PK11SymKey        *wrappingKey = nullptr;
+    PK11SymKey        *symKey = nullptr;
+    PK11SymKey        *token = nullptr;
+    SECStatus          retval;
+    SECItem           *secParam = nullptr;
+    SECItem            ivItem, wrappedKey;
+    unsigned char      output[48];   // Buffer to hold 256 bit key + padding
+    unsigned char      iv[16];       // Initialization vector for AES
+    unsigned int       outputLength;
+    int                i;
+
+    slot = PK11_GetBestSlot(CKM_AES_KEY_GEN, nullptr);
+    if (slot == nullptr) {
+        error(errInternal, -1, "Unable to find security device (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    // Generate random key for wrapping of given key by AES-256
+    wrappingKey = PK11_KeyGen(slot, CKM_AES_KEY_GEN, nullptr, 32, nullptr);
+    if (wrappingKey == nullptr) {
+        error(errInternal, -1, "Failed to generate wrapping key (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    for (i = 0; i < 16; i++)
+        iv[i] = i;
+
+    ivItem.type = siBuffer;
+    ivItem.data = iv;
+    ivItem.len = 16;
+
+    secParam = PK11_ParamFromIV(cipherMech, &ivItem);
+    if (secParam == nullptr) {
+        error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    // Encrypt given key
+    retval = PK11_Encrypt(wrappingKey,
+                          cipherMech,
+                          secParam,
+                          output,
+                          &outputLength,
+                          sizeof(output),
+                          key,
+                          keyLen);
+    if (retval != SECSuccess) {
+        error(errInternal, -1, "Failed to encrypt key (err {0:d})",
+              PR_GetError());
+    }
+
+    wrappedKey.type = siBuffer;
+    wrappedKey.data = output;
+    wrappedKey.len = outputLength;
+
+    // Unwrap the wrapped key to token so it can be used in FIPS mode
+    token = PK11_UnwrapSymKey(wrappingKey,
+                              cipherMech,
+                              &ivItem,
+                              &wrappedKey,
+                              operation,
+                              CKA_UNWRAP,
+                              keyLen);
+
+    if (token == nullptr) {
+        error(errInternal, -1, "Failed to unwrap symmetric key (err {0:d})",
+              PR_GetError());
+    }
+
+err:
+    if (secParam != nullptr)
+        SECITEM_FreeItem(secParam, PR_TRUE);
+
+    if (wrappingKey != nullptr)
+        PK11_FreeSymKey(wrappingKey);
+
+    if (symKey != nullptr)
+        PK11_FreeSymKey(symKey);
+
+    if (slot != nullptr)
+        PK11_FreeSlot(slot);
+
+    return token;
+}
+
+static PK11Context *rc4InitContext(const unsigned char *key, int keyLen) {
+    CK_MECHANISM_TYPE  cipherMech = CKM_RC4;
+    PK11SlotInfo      *slot = nullptr;
+    PK11SymKey        *symKey = nullptr;
+    SECItem           *secParam = nullptr;
+    PK11Context       *context = nullptr;
+
+    slot = PK11_GetBestSlot(cipherMech, nullptr);
+    if (slot == nullptr) {
+        error(errInternal, -1, "Unable to find security device (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    symKey = tokenizeKey(key, keyLen, cipherMech);
+    if (symKey == nullptr) {
+        error(errInternal, -1, "Failed to create token from key (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    secParam = PK11_ParamFromIV(cipherMech, nullptr);
+    if (secParam == nullptr) {
+        error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    context = PK11_CreateContextBySymKey(cipherMech,
+                                         CKA_DECRYPT,
+                                         symKey,
+                                         secParam);
+    if (context == nullptr) {
+        error(errInternal, -1, "Failed to create context (err {0:d})",
+              PR_GetError());
+    }
+
+err:
+    if (secParam != nullptr)
+        SECITEM_FreeItem(secParam, PR_TRUE);
+
+    if (symKey != nullptr)
+        PK11_FreeSymKey(symKey);
+
+    if (slot != nullptr)
+        PK11_FreeSlot(slot);
+
+    return context;
+}
+
+static unsigned char rc4DecryptByte(PK11Context *context, unsigned char c) {
+    unsigned char outputChar = 0;
+    SECStatus     retval;
+    int           outputLength;
+
+    retval = PK11_CipherOp(context,
+                           &outputChar,
+                           &outputLength,
+                           1,
+                           &c,
+                           1);
+
+    if (retval != SECSuccess) {
+        error(errInternal, -1, "Failed to decrypt byte (err {0:d})",
+              PR_GetError());
+    }
+
+    return outputChar;
+}
+
+#else
+
 static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state)
 {
     unsigned char index1, index2;
     unsigned char t;
@@ -609,6 +997,8 @@ static unsigned char rc4DecryptByte(unsigned char *sta
     return c ^ state[(tx + ty) % 256];
 }
 
+#endif
+
 //------------------------------------------------------------------------
 // AES decryption
 //------------------------------------------------------------------------
@@ -639,6 +1029,178 @@ static bool aesReadBlock(Stream *str, G
     }
 }
 
+#ifdef ENABLE_NSS3
+
+static PK11Context *aesInitContext(unsigned char *in, unsigned char *objKey,
+                                   int objKeyLength, bool decrypt) {
+    CK_MECHANISM_TYPE  cipherMech = CKM_AES_CBC;
+    CK_ATTRIBUTE_TYPE  operationType = decrypt ? CKA_DECRYPT : CKA_ENCRYPT;
+    PK11SlotInfo      *slot;
+    PK11SymKey        *symKey = nullptr;
+    SECItem           *secParam = nullptr;
+    PK11Context       *context = nullptr;
+    SECItem            ivItem;
+
+    slot = PK11_GetBestSlot(cipherMech, nullptr);
+    if (slot == nullptr) {
+        error(errInternal, -1, "Unable to find security device (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    symKey = tokenizeKey(objKey, objKeyLength, cipherMech);
+    if (symKey == nullptr) {
+        error(errInternal, -1, "Failed to create token from key (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    ivItem.type = siBuffer;
+    ivItem.data = in;
+    ivItem.len = 16;
+
+    secParam = PK11_ParamFromIV(cipherMech, &ivItem);
+    if (secParam == nullptr) {
+        error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    context = PK11_CreateContextBySymKey(cipherMech,
+                                         operationType,
+                                         symKey,
+                                         secParam);
+
+err:
+    if (secParam != nullptr)
+        SECITEM_FreeItem(secParam, PR_TRUE);
+
+    if (symKey != nullptr)
+        PK11_FreeSymKey(symKey);
+
+    if (slot != nullptr)
+        PK11_FreeSlot(slot);
+
+    return context;
+}
+
+static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in) {
+    SECStatus rv;
+    int       outputLength;
+
+    rv = PK11_CipherOp(s->context,
+                       s->buf,
+                       &outputLength,
+                       16,
+                       in,
+                       16);
+
+    if (rv != SECSuccess) {
+        error(errInternal, -1, "Failed to encrypt input block (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    s->bufIdx = 0;
+
+err:
+    return;
+}
+
+static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last) {
+    SECStatus rv1;
+    int       outputLen;
+    int       n, i;
+
+    rv1 = PK11_CipherOp(s->context,
+                        s->buf,
+                        &outputLen,
+                        16,
+                        in,
+                        16);
+
+    if (rv1 != SECSuccess) {
+        error(errInternal, -1, "Failed to decrypt input block (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    s->bufIdx = 0;
+    if (last) {
+        n = s->buf[15];
+        if (n < 1 || n > 16) { // this should never happen
+            n = 16;
+        }
+        for (i = 15; i >= n; --i) {
+            s->buf[i] = s->buf[i-n];
+        }
+        s->bufIdx = n;
+    }
+
+err:
+    return;
+}
+
+static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in) {
+    SECStatus rv;
+    int       outputLength;
+
+    rv = PK11_CipherOp(s->context,
+                       s->buf,
+                       &outputLength,
+                       16,
+                       in,
+                       16);
+
+    if (rv != SECSuccess) {
+        error(errInternal, -1, "Failed to encrypt input block (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    s->bufIdx = 0;
+
+err:
+    return;
+}
+
+static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in,
+                               bool last) {
+    SECStatus rv1;
+    int       outputLen;
+    int       n, i;
+
+    rv1 = PK11_CipherOp(s->context,
+                        s->buf,
+                        &outputLen,
+                        16,
+                        in,
+                        16);
+
+    if (rv1 != SECSuccess) {
+        error(errInternal, -1, "Failed to decrypt input block (err {0:d})",
+              PR_GetError());
+        goto err;
+    }
+
+    s->bufIdx = 0;
+    if (last) {
+        n = s->buf[15];
+        if (n < 1 || n > 16) { // this should never happen
+            n = 16;
+        }
+        for (i = 15; i >= n; --i) {
+            s->buf[i] = s->buf[i-n];
+        }
+        s->bufIdx = n;
+    }
+
+err:
+    return;
+}
+
+#else
+
 static const unsigned char sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
                                          0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
                                          0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
@@ -1121,10 +1683,33 @@ static void aes256DecryptBlock(DecryptAE
     }
 }
 
+#endif
+
 //------------------------------------------------------------------------
 // MD5 message digest
 //------------------------------------------------------------------------
 
+#ifdef ENABLE_NSS3
+static void hashFunc(const unsigned char *msg, int msgLen, unsigned char *hash,
+                     HASH_HashType type) {
+    HASHContext *context;
+    unsigned int hashLen = 0;
+
+    if (!initNSS())
+        return;
+
+    context = HASH_Create(type);
+    if (context == nullptr)
+        return;
+
+    HASH_Begin(context);
+    HASH_Update(context, msg, msgLen);
+    HASH_End(context, hash, &hashLen, HASH_ResultLen(type));
+    HASH_Destroy(context);
+}
+
+#else
+
 // this works around a bug in older Sun compilers
 static inline unsigned long rotateLeft(unsigned long x, int r)
 {
@@ -1151,8 +1736,13 @@ static inline unsigned long md5Round4(unsigned long a,
     state->digest[15] = (unsigned char)(state->d >> 24);
 }
 
+#endif
+
 void md5(const unsigned char *msg, int msgLen, unsigned char *digest)
 {
+#ifdef ENABLE_NSS3
+    hashFunc(msg, msgLen, digest, HASH_AlgMD5);
+#else
     if (msgLen < 0) {
         return;
     }
@@ -1296,12 +1886,14 @@ void md5(unsigned char *msg, int msgLen, unsigned char
     for (int i = 0; i < 16; ++i) {
         digest[i] = state.digest[i];
     }
+#endif
 }
 
 //------------------------------------------------------------------------
 // SHA-256 hash
 //------------------------------------------------------------------------
 
+#ifndef ENABLE_NSS3
 static const unsigned int sha256K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
                                           0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
                                           0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
@@ -1400,9 +1992,13 @@ static void sha256HashBlock(unsigned char *blk,
     H[6] += g;
     H[7] += h;
 }
+#endif
 
 static void sha256(unsigned char *msg, int msgLen, unsigned char *hash)
 {
+#ifdef ENABLE_NSS3
+    hashFunc(msg, msgLen, hash, HASH_AlgSHA256);
+#else
     unsigned char blk[64];
     unsigned int H[8];
     int blkLen, i;
@@ -1453,7 +2049,10 @@ static void sha256(unsigned char *msg, int msgL
         hash[i * 4 + 2] = (unsigned char)(H[i] >> 8);
         hash[i * 4 + 3] = (unsigned char)H[i];
     }
+#endif
 }
+
+#ifndef ENABLE_NSS3
 //------------------------------------------------------------------------
 // SHA-512 hash (see FIPS 180-4)
 //------------------------------------------------------------------------
@@ -1557,9 +2156,13 @@ static void sha512HashBlock(unsigned char *blk,
     H[6] += g;
     H[7] += h;
 }
+#endif
 
 static void sha512(unsigned char *msg, int msgLen, unsigned char *hash)
 {
+#ifdef ENABLE_NSS3
+    hashFunc(msg, msgLen, hash, HASH_AlgSHA512);
+#else
     unsigned char blk[128];
     uint64_t H[8];
     int blkLen = 0, i;
@@ -1622,6 +2225,7 @@ static void sha512(unsigned char *msg, int msgL
         hash[i * 8 + 6] = (unsigned char)(H[i] >> 8);
         hash[i * 8 + 7] = (unsigned char)H[i];
     }
+#endif
 }
 
 //------------------------------------------------------------------------
@@ -1631,6 +2235,9 @@ static void sha512(unsigned char *msg, int msgL
 // 2.A 384 bit message digest is obtained by truncating the final hash value.
 static void sha384(unsigned char *msg, int msgLen, unsigned char *hash)
 {
+#ifdef ENABLE_NSS3
+    hashFunc(msg, msgLen, hash, HASH_AlgSHA384);
+#else
     unsigned char blk[128];
     uint64_t H[8];
     int blkLen, i;
@@ -1696,6 +2303,7 @@ static void sha384(unsigned char *msg, int msgL
         hash[i * 8 + 6] = (unsigned char)(H[i] >> 8);
         hash[i * 8 + 7] = (unsigned char)H[i];
     }
+#endif
 }
 
 //------------------------------------------------------------------------
@@ -1735,7 +2344,11 @@ static void revision6Hash(GooString *inp
         memcpy(state.buf, state.cbc, 16); // Copy CBC IV to buf
         state.bufIdx = 0;
         state.paddingReached = false;
+#ifdef ENABLE_NSS3
+        state.context = aesInitContext(state.cbc, aesKey, 16, false);
+#else
         aesKeyExpansion(&state, aesKey, 16, false);
+#endif
 
         for (int i = 0; i < (4 * sequenceLength); i++) {
             aesEncryptBlock(&state, K1 + (16 * i));
@@ -1776,6 +2389,9 @@ static void revision6Hash(GooString *inp
             sha512(E, totalLength, K);
         }
         rounds++;
+#ifdef ENABLE_NSS3
+        PK11_DestroyContext(state.context, PR_TRUE);
+#endif
     }
     // the first 32 bytes of the final K are the output of the function.
 }
diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h
index d4667c8c..16fa9830 100644
--- a/poppler/Decrypt.h
+++ b/poppler/Decrypt.h
@@ -31,6 +32,9 @@
 #include "goo/GooString.h"
 #include "Object.h"
 #include "Stream.h"
+#ifdef ENABLE_NSS3
+#include <pk11pub.h>
+#endif
 
 //------------------------------------------------------------------------
 // Decrypt
@@ -73,14 +77,22 @@ private:
  * case of encryption. */
 struct DecryptRC4State
 {
+#ifdef ENABLE_NSS3
+    PK11Context *context;
+#else
     unsigned char state[256];
     unsigned char x, y;
+#endif
 };
 
 struct DecryptAESState
 {
+#ifdef ENABLE_NSS3
+    PK11Context *context;
+#else
     unsigned int w[44];
     unsigned char state[16];
+#endif
     unsigned char cbc[16];
     unsigned char buf[16];
     bool paddingReached; // encryption only
@@ -87,8 +99,12 @@ struct DecryptAESState {
 
 struct DecryptAES256State
 {
+#ifdef ENABLE_NSS3
+    PK11Context *context;
+#else
     unsigned int w[60];
     unsigned char state[16];
+#endif
     unsigned char cbc[16];
     unsigned char buf[16];
     bool paddingReached; // encryption only
diff --git a/poppler/poppler-config.h.cmake b/poppler/poppler-config.h.cmake
index f0a5a1a0..dcaade6f 100644
--- a/poppler/poppler-config.h.cmake
+++ b/poppler/poppler-config.h.cmake
@@ -115,6 +115,12 @@
 #cmakedefine USE_BOOST_HEADERS 1
 #endif
 
+/* Build against libnss3 for digital signature validation and
+   implementation of encryption/decryption. */
+#ifndef ENABLE_NSS3
+#cmakedefine ENABLE_NSS3 1
+#endif
+
 //------------------------------------------------------------------------
 // version
 //------------------------------------------------------------------------
