diff --git a/lib/modules/Standards.pmod/PKCS.pmod/PFX.pmod b/lib/modules/Standards.pmod/PKCS.pmod/PFX.pmod
new file mode 100644
index 0000000000000000000000000000000000000000..3fd7190a77bb1cde53223a16664e0f352271808a
--- /dev/null
+++ b/lib/modules/Standards.pmod/PKCS.pmod/PFX.pmod
@@ -0,0 +1,429 @@
+/* PFX.pmod
+ *
+ * M$ Personal Exchange Syntax and Protocol Standard, aka PKCS#12
+ *
+ * Subsets of PKCS#12 and PKCS#7 needed to import keys and
+ * certificates into Netscape and IE.
+ *
+ */
+
+import Standards.ASN1
+
+object pkcs_7_id = .Identifiers.pkcs_id->append(7);
+object data_id = pkcs_7_id->append(1);
+object signed_data_id = pkcs_7_id->append(2);
+object enveloped_data_id = pkcs_7_id->append(3);
+object signed_and_enveloped_data_id = pkcs_7_id->append(4);
+object digested_data_id = pkcs_7_id->append(5);
+object encrypted_data_id = pkcs_7_id->append(7);
+
+object pkcs_12_id = .Identifiers.pkcs_id->append(12);
+object pkcs_12_pbe_id = pkcs_12_id->append(1);
+object pbe_sha_rc4 = pkcs_12_pbe_id->append(1);
+object pbe_sha_rc4_weak = pkcs_12_pbe_id->append(2);
+object pbe_sha_3tripledes = pkcs_12_pbe_id->append(3);
+object pbe_sha_2triple_des = pkcs_12_pbe_id->append(4);
+object pbe_sha_rc2= pkcs_12_pbe_id->append(5);
+object pbe_sha_rc2_weak = pkcs_12_pbe_id->append(6);
+
+object pkcs_12_version1_id = pkcs_12_id->append(10);
+object pkcs_12_bag_id = pkcs_12_version1_id->append(1);
+object keybag_id = pkcs_12_bag_id->append(1);
+object pkcs_8_shroudedkeybag_id = pkcs_12_bag_id->append(2);
+object certbag_id = pkcs_12_bag_id->append(3);
+object crlbag_id = pkcs_12_bag_id->append(4);
+object secretbag_id = pkcs_12_bag_id->append(5);
+object safebag_id = pkcs_12_bag_id->append(6);
+
+object pkcs_9_id = .Identifiers.pkcs_id->append(9);
+
+object certTypes_id = pkcs_9_id->append(22);
+object x509Certificate_id = certTypes_id->append(1);
+
+/* Perhaps ContentInfo should be moved into a separate module, with
+   other PKCS#7 stuff? */
+
+class ContentInfo_meta
+{
+  /* Maps DER-encoded identifiers to corresponding content types
+   * (including explicit tags) */
+  mapping(string:function) content_types;
+
+  class `()
+    {
+      inherit asn1_sequence;
+  
+      mapping element_types(int i, mapping t)
+	{
+	  switch(i)
+	  {
+	  case 0:
+	    return ([ 6 : asn1_identifier ]);
+	  case 1:
+	    return ([ 0 : content_types[elements[0]->get_der()] ]);
+	  default:
+	    error("ContentInfo->element_types: Bad index\n");
+	  }
+	}
+      
+      object set_types(mapping(string:function) types)
+	{
+	  content_types = types;
+	  return this_object();
+	}
+
+      object end_decode_constructed(int length)
+	{
+	  if (length > 2)
+	    error("ContentInfo->end_decode_constructed: Bad index\n");
+	  return this_object();
+	}
+  
+#if 0
+      object decode_constructed(array contents, string raw)
+	{
+	  asn1_sequence::decode_constructed(contents, raw);
+	  if (!sizeof(elements)
+	      || (elements[0] != "Identifier")
+	      || (sizeof(elements) > 2))
+	    return 0;
+
+	  function p = content_types[elements[0]->get_der()];
+	  if (sizeof(elements) == 1)
+	  {
+	    /* No contents */
+	    if (p)
+	      /* No content expected */
+	      return 0;
+	  } else {
+	    /* Decode contents */
+	    if (!p)
+	      return 0;
+	    elements[1] = p(elements[1]);
+	  }
+	  return this_object();
+	}
+#endif
+
+      object init(object type, object contents)
+	{
+	  /* Neglects the valid_types field of meta_explicit */
+	  return ::init( ({ type, meta_explicit(0, 0)()->init(contents) }) );
+	}
+
+#if 0
+      string get_data_der()
+	{
+	  return elements[1]->get_der();
+	}
+#endif
+
+    }
+  
+  void create(mapping|void types)
+    {
+      content_types = types;
+    }
+}
+
+/* PFX definition, taken from the preview of PKCS#12 version 3
+   
+   PFX ::= SEQUENCE {
+   	       version         Version    -- V3(3) for this version.  This
+                                          -- field is not optional.
+   	       authSafes       ContentInfo,    -- from PKCS #7 v1.5
+   			       -- SignedData in public-key integrity mode
+   			       -- Data in password integrity mode
+   	       macData         MacData OPTIONAL }
+   
+   
+   MacData ::= SEQUENCE {
+   	       mac      DigestInfo,    -- from PKCS #7 v1.5
+   	       macSalt  OCTET STRING,
+   	       macIterationCount  INTEGER DEFAULT 1 }
+   	       -- if you want to be compatible with a certain release from
+   	       -- Microsoft, you should use the value 1 and not place the
+   	       -- macIterationCount field's encoding in the PDU's
+   	       -- BER-encoding.  Unfortunately, using a value of 1 here
+   	       -- means that there's no point in having a value other
+   	       -- than 1 for any password-based encryption in the PDU that
+   	       -- uses the same password as is used for password-based
+   	       -- authentication
+   
+   AuthenticatedSafes ::= 	SEQUENCE OF ContentInfo    -- from PKCS #7 v1.5
+   			   -- Data if unencrypted
+   			   -- EncryptedData if password-encrypted
+   			   -- EnvelopedData if public-key-encrypted
+   
+   
+   pkcs-12PbeParams ::= SEQUENCE {
+   	       salt           OCTET STRING,
+   	       iterationCount INTEGER }
+   
+   
+   pkcs-12PbeIds OBJECT IDENTIFIER ::= { pkcs-12 1 }
+   pbeWithSHA1And128BitRC4 OBJECT IDENTIFIER ::= { pkcs-12PbeIds 1 }
+   pbeWithSHA1And40BitRC4 OBJECT IDENTIFIER ::= { pkcs-12PbeIds 2 }
+   -- both triple-DES pbe OIDs do EDE (encrypt-decrypt-encrypt)
+   pbeWithSHA1And3-KeyTripleDES-CBC OBJECT IDENTIFIER ::=
+     { pkcs-12PbeIds 3 }
+   pbeWithSHA1And2-KeyTripleDES-CBC OBJECT IDENTIFIER ::=
+     { pkcs-12PbeIds 4 }
+   -- pbeWithSHA1And128BitRC2-CBC uses an effective keyspace search
+   -- size of 128 bits, as well as a 128-bit key
+   pbeWithSHA1And128BitRC2-CBC OBJECT IDENTIFIER ::=
+     { pkcs-12PbeIds 5 }
+   -- pbeWithSHA1And40BitRC2-CBC uses an effective keyspace search
+   -- size of 40 bits, as well as a 40-bit key
+   pbeWithSHA1And40BitRC2-CBC OBJECT IDENTIFIER ::=
+     { pkcs-12PbeIds 6 }
+   
+   
+   SafeContents ::= SEQUENCE OF SafeBag
+   
+   SafeBag ::= SEQUENCE {
+   	       bagType       OBJECT IDENTIFIER,
+   	       bagContent    [0] EXPLICIT ANY DEFINED BY bagType,
+   	       bagAttributes Attributes OPTIONAL }
+   
+   
+   Attributes ::= SET OF Attribute    -- from X.501
+   -- in pre-1994 ASN.1, Attribute looks like:
+   -- Attribute ::= SEQUENCE {
+   --      type OBJECT IDENTIFIER,
+   --      values SET OF ANY DEFINED BY type }
+   
+   
+   FriendlyName ::= BMPString    -- a friendlyName has a single attr. value
+   LocalKeyID   ::= OCTET STRING    -- a localKeyID has a single attr.
+   value
+   
+   
+   friendlyName OBJECT IDENTIFIER ::= { PKCS-9 20 }
+   localKeyID   OBJECT IDENTIFIER ::= { PKCS-9 21 }
+   
+   
+   KeyBag ::= PrivateKeyInfo    -- from PKCS #8 v1.2
+   
+   PKCS-8ShroudedKeyBag ::= EncryptedPrivateKeyInfo    -- from PKCS #8 v1.2
+   
+   CertBag   ::= SEQUENCE {
+   	     certType    OBJECT IDENTIFIER,
+   	     cert        EXPLICIT [0] ANY DEFINED BY certType }
+   
+   CRLBag    ::= SEQUENCE {
+   	     crlType     OBJECT IDENTIFIER,
+   	     crl         EXPLICIT [0] ANY DEFINED BY crlType }
+   
+   SecretBag ::= SEQUENCE {
+   	     secretType  OBJECT IDENTIFIER,
+   	     secret      EXPLICIT [0] ANY DEFINED BY secretType }
+   
+   SafeContentsBag ::= SafeContents
+   
+   
+   pkcs-12Version1      OBJECT IDENTIFIER ::= { pkcs-12 10 }
+   pkcs-12BagIds        OBJECT IDENTIFIER ::= { pkcs-12Version1 1}
+   keyBag               OBJECT IDENTIFIER ::= { pkcs-12BagIds 1 }
+   pkcs-8ShroudedKeyBag OBJECT IDENTIFIER ::= { pkcs-12BagIds 2 }
+   certBag              OBJECT IDENTIFIER ::= { pkcs-12BagIds 3 }
+   crlBag               OBJECT IDENTIFIER ::= { pkcs-12BagIds 4 }
+   secretBag            OBJECT IDENTIFIER ::= { pkcs-12BagIds 5 }
+   safeContentsBag      OBJECT IDENTIFIER ::= { pkcs-12BagIds 6 }
+   
+   
+   certTypes       OBJECT IDENTIFIER ::= { PKCS-9 22 }
+   X509Certificate                   ::= OCTET STRING
+   SDSICertificate                   ::= IA5String
+   x509Certificate OBJECT IDENTIFIER ::= { certTypes 1 }
+   sdsiCertificate OBJECT IDENTIFIER ::= { certTypes 2 }
+   
+   
+   crlTypes OBJECT IDENTIFIER ::= { PKCS-9 23 }
+   X509Crl                    ::= OCTET STRING
+   x509Crl  OBJECT IDENTIFIER ::= { crlTypes 1 }
+*/
+
+/* Same as PKCS#8 PrivateKeyInfo */
+class KeyBag
+{
+  inherit asn1_sequence;
+
+  object init(object algorithm_id, string|object key,
+	      void|object attr)
+    {
+      if (stringp(key))
+	key = asn1_octet_string(key);
+
+      array a = ({ asn1_integer(0), algorithm_id, key });
+      if (attr)
+	a += ({ attr });
+
+      return ::init(a);
+    }
+}
+
+/* Defaults for generated MAC:s */
+   
+#define SALT_SIZE 17
+#define MAC_COUNT 1
+
+class PFX
+{
+  inherit asn1_sequence;
+
+  object safes;
+  string passwd; /* Assumed to be latin1 */
+
+  object init(object s)
+    {
+      safes = s;
+      return this_object();
+    }
+
+  object set_signature_key(object key)
+    {
+      error("pfx->sign: Not implemented\n");
+    }
+
+  string latin1_to_bmp(string s)
+    {
+      /* String of 16 bit characters in big-endian order, terminated
+       * by a null character */
+      return "\0" + (s/"") * "\0" + "\0\0";
+    }
+  
+  /* passwd is assumed to be latin 1 */
+  object set_passwd(string s)
+    {
+      passwd = latin1_to_bmp(passwd);
+    }
+
+  string string_pad(string d, int block_size)
+    {
+      int s = sizeof(d);
+
+      if (s)
+      { /* Extend to a multiple of the block soze */
+	int n = (s + 63) / block_size; // number of blocks 
+	int k = (n+s-1) / s;
+	d = (d * k) [..n*block_size-1];
+      }
+      return d;
+    }
+  
+  string generate_key(string salt, int id, int count, int needed)
+    { /* Supports only SHA-1 */
+      string D = sprintf("%c", id) * 64;
+
+      string I = string_pad(salt, 64) + string_pad(passwd, 64);
+
+      string A = D+I;
+
+      for(int i; i<count; i++)
+	A = Crypto.sha()->update(A)->digest;
+
+      if (strlen(A)<needed)
+	error("PFX: Step 6c) of section 6.1 not implemented.\n");
+
+      return A[..ndeded-1];
+    }
+
+  string get_hmac(string salt, int count)
+    {
+      string key = generate_key(salt, 3, count, 20);
+
+      return Crypto.hmac(Crypto.sha)(key)
+	// Extract value from the data field
+	(elements[1]->elements[1]->value);
+    }
+  
+  string der_encode()
+    {
+      elements = allocate(2 + !!passwd);
+      elements[0] = asn1_integer(3); // version
+      elements[1] = safes;
+      if (passwd)
+      {	/* Password-integrity mode */
+	salt = Crypto.randomness.reasonably_random()->read(SALT_SIZE);
+
+	elements[2] = asn1_sequence(
+	  ({ asn1_sequence(
+	    ({ Identifiers.sha_id,
+	       asn1_octet_string(get_hmac(salt, MAC_COUNT)) }) ),
+	     asn1_octet_string(salt)
+	     /* , optional count, default = 1 */
+	  }) );
+	
+      } else {
+	error("Only passwd authentication supported\n");
+      }
+    }
+
+  int uses_passwd_integrity()
+    {
+      return elements[1]->elements[0] == data_id;
+    }
+  
+  int verify_passwd()
+    {
+      if (elements[2]->elements[0]->elements[0] != Identifiers.sha1_id)
+	error("Unexpected hash algorithm\n");
+      string salt = elements[2]->elements[1]->value;
+      int count = (sizeof(elements[2]->elements) == 3)
+	? (int) elements[2]->elements[2]->value : 1;
+      if (count < 1)
+	error("Bad count\n");
+
+      return (elements[2]->elements[0]->elements[1]->value
+	      == get_hmac(salt, count));
+    }
+}
+
+class SafeBag_meta
+{
+  object type; /* Object identifier */
+
+  class
+/* PrivateKeyInfo (pkcs#8), aka KeyBag (pkcs#12) */
+
+/* Note that the ASN.1 type is a sequence of the structure this
+ * function creates */
+object make_key_info(object id, string key)
+{
+  return asn1_sequence( ({ asn1_integer(0),
+			   id, asnt_octet_string(key) }) );
+}
+
+object make safe_bag(object id, object contents, object|void attributes)
+{
+  return asn1_sequence( ({ id, contents })
+			+ (attributes ? ({ attributes }) : ({ }) ));
+}
+  
+/* A SafeBag instance, with type of KeyBag */
+object make_key_bag(array keys, object|void attributes)
+{
+  return make_safe_bag(keybag_id, asn1_sequence(keys), attributes);
+}
+
+/* A safe bag of type certBag, containing a certBag of type x509Certificate */
+object make x509_cert_bag(string cert, object|void attributes)
+{
+  return asn1_sequence(certbag_id,
+		       asn1_sequence( ({ x509Certificate_id, cert }) ),
+		       attributes);
+}
+
+object make_safecontents(object ...bags)
+{
+  return asn1_sequence(
+/* Makes a PFX of unencrypted bags */
+object simple_make_pfx(array bags, string passwd)
+{
+  object safe_contents = asn1_sequence(bags);
+  
+  object pfx = PFX(ContentInfo_meta()(data_id,
+				      asn1_string(safes->get_der())));
+  pfx->set_passwd(passwd);
+}