diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js
index 9626a693..dff3ea7a 100644
--- a/src/crypto/crypto.js
+++ b/src/crypto/crypto.js
@@ -36,13 +36,12 @@ import publicKey from './public_key';
 import cipher from './cipher';
 import random from './random';
 import type_ecdh_symkey from '../type/ecdh_symkey';
-import type_kdf_params from '../type/kdf_params';
+import KDFParams from '../type/kdf_params';
 import type_mpi from '../type/mpi';
-import type_oid from '../type/oid';
 import enums from '../enums';
 import util from '../util';
-import pkcs1 from './pkcs1';
-import pkcs5 from './pkcs5';
+import OID from '../type/oid';
+import Curve from './public_key/elliptic/curves';
 
 function constructParams(types, data) {
   return types.map(function(type, i) {
@@ -57,40 +56,30 @@ export default {
   /**
    * Encrypts data using specified algorithm and public key parameters.
    * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
-   * @param {module:enums.publicKey}        algo        Public key algorithm
-   * @param {Array<module:type/mpi|
-                   module:type/oid|
-                   module:type/kdf_params>} pub_params  Algorithm-specific public key parameters
-   * @param {Uint8Array}                    data        Data to be encrypted
-   * @param {Uint8Array}                    fingerprint Recipient fingerprint
+   * @param {module:enums.publicKey}    algo        Public key algorithm
+   * @param {Object}                    pubParams  Algorithm-specific public key parameters
+   * @param {Uint8Array}                data        Data to be encrypted
+   * @param {Uint8Array}                fingerprint Recipient fingerprint
    * @returns {Array<module:type/mpi|
    *                 module:type/ecdh_symkey>}          encrypted session key parameters
    * @async
    */
-  publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) {
+  publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
     const types = this.getEncSessionKeyParamTypes(algo);
     switch (algo) {
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaEncryptSign: {
-        const n = pub_params[0].toUint8Array();
-        const e = pub_params[1].toUint8Array();
+        const { n, e } = publicParams;
         const res = await publicKey.rsa.encrypt(data, n, e);
         return constructParams(types, [res]);
       }
       case enums.publicKey.elgamal: {
-        data = new type_mpi(await pkcs1.eme.encode(data, pub_params[0].byteLength()));
-        const m = await data.toBigInteger();
-        const p = await pub_params[0].toBigInteger();
-        const g = await pub_params[1].toBigInteger();
-        const y = await pub_params[2].toBigInteger();
-        const res = await publicKey.elgamal.encrypt(m, p, g, y);
+        const { p, g, y } = publicParams;
+        const res = await publicKey.elgamal.encrypt(data, p, g, y);
         return constructParams(types, [res.c1, res.c2]);
       }
       case enums.publicKey.ecdh: {
-        data = new type_mpi(pkcs5.encode(data));
-        const oid = pub_params[0];
-        const Q = pub_params[1].toUint8Array();
-        const kdfParams = pub_params[2];
+        const { oid, Q, kdfParams } = publicParams;
         const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
           oid, kdfParams, data, Q, fingerprint);
         return constructParams(types, [V, C]);
@@ -104,125 +93,133 @@ export default {
    * Decrypts data using specified algorithm and private key parameters.
    * See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
    * @param {module:enums.publicKey}        algo        Public key algorithm
-   * @param {Array<module:type/mpi|
-                   module:type/oid|
-                   module:type/kdf_params>} key_params  Algorithm-specific public, private key parameters
+   * @param {Object}                        publicKeyParams   Algorithm-specific public key parameters
+   * @param {Object}                        privateKeyParams  Algorithm-specific private key parameters
    * @param {Array<module:type/mpi|
                    module:type/ecdh_symkey>}
                                             data_params encrypted session key parameters
-   * @param {String}                        fingerprint Recipient fingerprint
-   * @returns {String}                          String containing the decrypted data
+   * @param {Uint8Array}                    fingerprint Recipient fingerprint
+   * @returns {Uint8Array}                  decrypted data
    * @async
    */
-  publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) {
+  publicKeyDecrypt: async function(algo, publicKeyParams, privateKeyParams, data_params, fingerprint) {
     switch (algo) {
       case enums.publicKey.rsaEncryptSign:
       case enums.publicKey.rsaEncrypt: {
         const c = data_params[0].toUint8Array();
-        const n = key_params[0].toUint8Array(); // n = pq
-        const e = key_params[1].toUint8Array();
-        const d = key_params[2].toUint8Array(); // de = 1 mod (p-1)(q-1)
-        const p = key_params[3].toUint8Array();
-        const q = key_params[4].toUint8Array();
-        const u = key_params[5].toUint8Array(); // p^-1 mod q
+        const { n, e } = publicKeyParams;
+        const { d, p, q, u } = privateKeyParams;
         return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
       }
       case enums.publicKey.elgamal: {
-        const c1 = await data_params[0].toBigInteger();
-        const c2 = await data_params[1].toBigInteger();
-        const p = await key_params[0].toBigInteger();
-        const x = await key_params[3].toBigInteger();
-        const result = new type_mpi(await publicKey.elgamal.decrypt(c1, c2, p, x));
-        return pkcs1.eme.decode(result.toUint8Array('be', p.byteLength()));
+        const c1 = data_params[0].toUint8Array();
+        const c2 = data_params[1].toUint8Array();
+        const p = publicKeyParams.p;
+        const x = privateKeyParams.x;
+        return publicKey.elgamal.decrypt(c1, c2, p, x);
       }
       case enums.publicKey.ecdh: {
-        const oid = key_params[0];
-        const kdfParams = key_params[2];
+        const { oid, Q, kdfParams } = publicKeyParams;
+        const { d } = privateKeyParams;
         const V = data_params[0].toUint8Array();
         const C = data_params[1].data;
-        const Q = key_params[1].toUint8Array();
-        const d = key_params[3].toUint8Array();
-        const result = new type_mpi(await publicKey.elliptic.ecdh.decrypt(
-          oid, kdfParams, V, C, Q, d, fingerprint));
-        return pkcs5.decode(result.toUint8Array());
+        return publicKey.elliptic.ecdh.decrypt(
+          oid, kdfParams, V, C, Q, d, fingerprint);
       }
       default:
         throw new Error('Invalid public key encryption algorithm.');
     }
   },
 
-  /** Returns the types comprising the private key of an algorithm
-   * @param {module:enums.publicKey}  algo The public key algorithm
-   * @returns {Array<Object>}         The array of types
+  /**
+   * Parse public key material in binary form to get the key parameters
+   * @param {module:enums.publicKey} algo  The key algorithm
+   * @param {Uint8Array}             bytes The key material to parse
+   * @returns {Object} key parameters referenced by name
+   * @returns { read: Number, publicParams: Object } number of read bytes plus key parameters referenced by name
    */
-  getPrivKeyParamTypes: function(algo) {
+  parsePublicKeyParams: function(algo, bytes) {
+    let read = 0;
     switch (algo) {
-      //   Algorithm-Specific Fields for RSA secret keys:
-      //       - multiprecision integer (MPI) of RSA secret exponent d.
-      //       - MPI of RSA secret prime value p.
-      //       - MPI of RSA secret prime value q (p < q).
-      //       - MPI of u, the multiplicative inverse of p, mod q.
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaEncryptSign:
-      case enums.publicKey.rsaSign:
-        return [type_mpi, type_mpi, type_mpi, type_mpi];
-      //   Algorithm-Specific Fields for Elgamal secret keys:
-      //        - MPI of Elgamal secret exponent x.
-      case enums.publicKey.elgamal:
-        return [type_mpi];
-      //   Algorithm-Specific Fields for DSA secret keys:
-      //      - MPI of DSA secret exponent x.
-      case enums.publicKey.dsa:
-        return [type_mpi];
-      //   Algorithm-Specific Fields for ECDSA or ECDH secret keys:
-      //       - MPI of an integer representing the secret key.
-      case enums.publicKey.ecdh:
-      case enums.publicKey.ecdsa:
-      case enums.publicKey.eddsa:
-        return [type_mpi];
+      case enums.publicKey.rsaSign: {
+        let read = 0;
+        const n = util.readMPI(bytes.subarray(read)); read += n.length + 2;
+        const e = util.readMPI(bytes.subarray(read)); read += e.length + 2;
+        return { read, publicParams: { n, e } };
+      }
+      case enums.publicKey.dsa: {
+        const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
+        const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
+        const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
+        const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
+        return { read, publicParams: { p, q, g, y } };
+      }
+      case enums.publicKey.elgamal: {
+        const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
+        const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
+        const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
+        return { read, publicParams: { p, g, y } };
+      }
+      case enums.publicKey.ecdsa: {
+        const oid = new OID(); read += oid.read(bytes);
+        const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
+        return { read: read, publicParams: { oid, Q } };
+      }
+      case enums.publicKey.eddsa: {
+        const oid = new OID(); read += oid.read(bytes);
+        let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
+        Q = util.padToLength(Q, 33);
+        return { read: read, publicParams: { oid, Q } };
+      }
+      case enums.publicKey.ecdh: {
+        const oid = new OID(); read += oid.read(bytes);
+        const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
+        const kdfParams = new KDFParams(); read += kdfParams.read(bytes.subarray(read));
+        return { read: read, publicParams: { oid, Q, kdfParams } };
+      }
       default:
         throw new Error('Invalid public key encryption algorithm.');
     }
   },
 
-  /** Returns the types comprising the public key of an algorithm
-   * @param {module:enums.publicKey}  algo The public key algorithm
-   * @returns {Array<Object>}         The array of types
+  /**
+   * Parse private key material in binary form to get the key parameters
+   * @param {module:enums.publicKey} algo  The key algorithm
+   * @param {Uint8Array}             bytes The key material to parse
+   * @param {Object}                 publicParams (ECC only) public params, needed to format some private params
+   * @returns { read: Number, privateParams: Object } number of read bytes plus the key parameters referenced by name
    */
-  getPubKeyParamTypes: function(algo) {
+  parsePrivateKeyParams: function(algo, bytes, publicParams) {
+    let read = 0;
     switch (algo) {
-      //   Algorithm-Specific Fields for RSA public keys:
-      //       - a multiprecision integer (MPI) of RSA public modulus n;
-      //       - an MPI of RSA public encryption exponent e.
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaEncryptSign:
-      case enums.publicKey.rsaSign:
-        return [type_mpi, type_mpi];
-      //   Algorithm-Specific Fields for Elgamal public keys:
-      //       - MPI of Elgamal prime p;
-      //       - MPI of Elgamal group generator g;
-      //       - MPI of Elgamal public key value y (= g**x mod p where x  is secret).
-      case enums.publicKey.elgamal:
-        return [type_mpi, type_mpi, type_mpi];
-      //   Algorithm-Specific Fields for DSA public keys:
-      //       - MPI of DSA prime p;
-      //       - MPI of DSA group order q (q is a prime divisor of p-1);
-      //       - MPI of DSA group generator g;
-      //       - MPI of DSA public-key value y (= g**x mod p where x  is secret).
+      case enums.publicKey.rsaSign: {
+        const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
+        const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
+        const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
+        const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
+        return { read, privateParams: { d, p, q, u } };
+      }
       case enums.publicKey.dsa:
-        return [type_mpi, type_mpi, type_mpi, type_mpi];
-      //   Algorithm-Specific Fields for ECDSA/EdDSA public keys:
-      //       - OID of curve;
-      //       - MPI of EC point representing public key.
+      case enums.publicKey.elgamal: {
+        const x = util.readMPI(bytes.subarray(read)); read += x.length + 2;
+        return { read, privateParams: { x } };
+      }
       case enums.publicKey.ecdsa:
-      case enums.publicKey.eddsa:
-        return [type_oid, type_mpi];
-      //   Algorithm-Specific Fields for ECDH public keys:
-      //       - OID of curve;
-      //       - MPI of EC point representing public key.
-      //       - KDF: variable-length field containing KDF parameters.
-      case enums.publicKey.ecdh:
-        return [type_oid, type_mpi, type_kdf_params];
+      case enums.publicKey.ecdh: {
+        const curve = new Curve(publicParams.oid);
+        let d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
+        d = util.padToLength(d, curve.payloadSize);
+        return { read, privateParams: { d } };
+      }
+      case enums.publicKey.eddsa: {
+        let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2;
+        seed = util.padToLength(seed, 32);
+        return { read, privateParams: { seed } };
+      }
       default:
         throw new Error('Invalid public key encryption algorithm.');
     }
@@ -255,42 +252,60 @@ export default {
     }
   },
 
-  /** Generate algorithm-specific key parameters
+  /**
+   * Convert params to MPI and serializes them in the proper order
+   * @param {module:enums.publicKey}  algo       The public key algorithm
+   * @param {Object}                  params     The key parameters indexed by name
+   * @returns {Uint8Array}  The array containing the MPIs
+   */
+  serializeKeyParams: function(algo, params) {
+    const orderedParams = Object.keys(params).map(name => {
+      const param = params[name];
+      return util.isUint8Array(param) ? util.uint8ArrayToMpi(param) : param.write();
+    });
+    return util.concatUint8Array(orderedParams);
+  },
+
+  /**
+   * Generate algorithm-specific key parameters
    * @param {module:enums.publicKey}  algo The public key algorithm
    * @param {Integer}                 bits Bit length for RSA keys
    * @param {module:type/oid}         oid  Object identifier for ECC keys
-   * @returns {Array}                 The array of parameters
+   * @returns { publicParams, privateParams: {Object} } The parameters referenced by name
    * @async
    */
   generateParams: function(algo, bits, oid) {
-    const types = [].concat(this.getPubKeyParamTypes(algo), this.getPrivKeyParamTypes(algo));
     switch (algo) {
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaEncryptSign:
       case enums.publicKey.rsaSign: {
-        return publicKey.rsa.generate(bits, 65537).then(function(keyObject) {
-          return constructParams(
-            types, [keyObject.n, keyObject.e, keyObject.d, keyObject.p, keyObject.q, keyObject.u]
-          );
-        });
+        return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({
+          privateParams: { d, p, q, u },
+          publicParams: { n, e }
+        }));
       }
+      case enums.publicKey.ecdsa:
+        return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
+          privateParams: { d: secret },
+          publicParams: { oid: new OID(oid), Q }
+        }));
+      case enums.publicKey.eddsa:
+        return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
+          privateParams: { seed: secret },
+          publicParams: { oid: new OID(oid), Q }
+        }));
+      case enums.publicKey.ecdh:
+        return publicKey.elliptic.generate(oid).then(({ oid, Q, secret, hash, cipher }) => ({
+          privateParams: { d: secret },
+          publicParams: {
+            oid: new OID(oid),
+            Q,
+            kdfParams: new KDFParams({ hash, cipher })
+          }
+        }));
       case enums.publicKey.dsa:
       case enums.publicKey.elgamal:
         throw new Error('Unsupported algorithm for key generation.');
-      case enums.publicKey.ecdsa:
-      case enums.publicKey.eddsa:
-        return publicKey.elliptic.generate(oid).then(function (keyObject) {
-          return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]);
-        });
-      case enums.publicKey.ecdh:
-        return publicKey.elliptic.generate(oid).then(function (keyObject) {
-          return constructParams(types, [
-            keyObject.oid,
-            keyObject.Q,
-            { hash: keyObject.hash, cipher: keyObject.cipher },
-            keyObject.d
-          ]);
-        });
       default:
         throw new Error('Invalid public key algorithm.');
     }
@@ -299,65 +314,43 @@ export default {
   /**
    * Validate algorithm-specific key parameters
    * @param {module:enums.publicKey}  algo The public key algorithm
-   * @param {Array}                   params The array of parameters
-   * @returns {Promise<Boolean>}       whether the parameters are valid
+   * @param {Object}                  publicParams Algorithm-specific public key parameters
+   * @param {Object}                  privateParams Algorithm-specific private key parameters
+   * @returns {Promise<Boolean>}      whether the parameters are valid
    * @async
    */
-  validateParams: async function(algo, params) {
+  validateParams: async function(algo, publicParams, privateParams) {
+    if (!publicParams || !privateParams) {
+      throw new Error('Missing key parameters');
+    }
     switch (algo) {
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaEncryptSign:
       case enums.publicKey.rsaSign: {
-        if (params.length < 6) {
-          throw new Error('Missing key parameters');
-        }
-        const n = params[0].toUint8Array();
-        const e = params[1].toUint8Array();
-        const d = params[2].toUint8Array();
-        const p = params[3].toUint8Array();
-        const q = params[4].toUint8Array();
-        const u = params[5].toUint8Array();
+        const { n, e } = publicParams;
+        const { d, p, q, u } = privateParams;
         return publicKey.rsa.validateParams(n, e, d, p, q, u);
       }
       case enums.publicKey.dsa: {
-        if (params.length < 5) {
-          throw new Error('Missing key parameters');
-        }
-        const p = params[0].toUint8Array();
-        const q = params[1].toUint8Array();
-        const g = params[2].toUint8Array();
-        const y = params[3].toUint8Array();
-        const x = params[4].toUint8Array();
+        const { p, q, g, y } = publicParams;
+        const { x } = privateParams;
         return publicKey.dsa.validateParams(p, q, g, y, x);
       }
       case enums.publicKey.elgamal: {
-        if (params.length < 4) {
-          throw new Error('Missing key parameters');
-        }
-        const p = params[0].toUint8Array();
-        const g = params[1].toUint8Array();
-        const y = params[2].toUint8Array();
-        const x = params[3].toUint8Array();
+        const { p, g, y } = publicParams;
+        const { x } = privateParams;
         return publicKey.elgamal.validateParams(p, g, y, x);
       }
       case enums.publicKey.ecdsa:
       case enums.publicKey.ecdh: {
-        const expectedLen = algo === enums.publicKey.ecdh ? 3 : 2;
-        if (params.length < expectedLen) {
-          throw new Error('Missing key parameters');
-        }
-
         const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
-        const { oid, Q, d } = algoModule.parseParams(params);
+        const { oid, Q } = publicParams;
+        const { d } = privateParams;
         return algoModule.validateParams(oid, Q, d);
       }
       case enums.publicKey.eddsa: {
-        const expectedLen = 3;
-        if (params.length < expectedLen) {
-          throw new Error('Missing key parameters');
-        }
-
-        const { oid, Q, seed } = publicKey.elliptic.eddsa.parseParams(params);
+        const { oid, Q } = publicParams;
+        const { seed } = privateParams;
         return publicKey.elliptic.eddsa.validateParams(oid, Q, seed);
       }
       default:
diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js
index 1a712a82..83dca363 100644
--- a/src/crypto/public_key/dsa.js
+++ b/src/crypto/public_key/dsa.js
@@ -36,16 +36,21 @@ export default {
    * DSA Sign function
    * @param {Integer} hash_algo
    * @param {Uint8Array} hashed
-   * @param {BigInteger} g
-   * @param {BigInteger} p
-   * @param {BigInteger} q
-   * @param {BigInteger} x
-   * @returns {{ r: BigInteger, s: BigInteger }}
+   * @param {Uint8Array} g
+   * @param {Uint8Array} p
+   * @param {Uint8Array} q
+   * @param {Uint8Array} x
+   * @returns {{ r: Uint8Array, s: Uint8Array }}
    * @async
    */
   sign: async function(hash_algo, hashed, g, p, q, x) {
     const BigInteger = await util.getBigInteger();
     const one = new BigInteger(1);
+    p = new BigInteger(p);
+    q = new BigInteger(q);
+    g = new BigInteger(g);
+    x = new BigInteger(x);
+
     let k;
     let r;
     let s;
@@ -87,30 +92,37 @@ export default {
   /**
    * DSA Verify function
    * @param {Integer} hash_algo
-   * @param {BigInteger} r
-   * @param {BigInteger} s
+   * @param {Uint8Array} r
+   * @param {Uint8Array} s
    * @param {Uint8Array} hashed
-   * @param {BigInteger} g
-   * @param {BigInteger} p
-   * @param {BigInteger} q
-   * @param {BigInteger} y
+   * @param {Uint8Array} g
+   * @param {Uint8Array} p
+   * @param {Uint8Array} q
+   * @param {Uint8Array} y
    * @returns {boolean}
    * @async
    */
   verify: async function(hash_algo, r, s, hashed, g, p, q, y) {
     const BigInteger = await util.getBigInteger();
     const zero = new BigInteger(0);
+    r = new BigInteger(r);
+    s = new BigInteger(s);
+
+    p = new BigInteger(p);
+    q = new BigInteger(q);
+    g = new BigInteger(g);
+    y = new BigInteger(y);
 
     if (r.lte(zero) || r.gte(q) ||
         s.lte(zero) || s.gte(q)) {
       util.printDebug("invalid DSA Signature");
-      return null;
+      return false;
     }
     const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
     const w = s.modInv(q); // s**-1 mod q
     if (w.isZero()) {
       util.printDebug("invalid DSA Signature");
-      return null;
+      return false;
     }
 
     g = g.mod(p);
diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js
index 30c7cc0a..9e611cc0 100644
--- a/src/crypto/public_key/elgamal.js
+++ b/src/crypto/public_key/elgamal.js
@@ -24,38 +24,55 @@
 
 import util from '../../util';
 import random from '../random';
+import pkcs1 from '../pkcs1';
 
 export default {
   /**
    * ElGamal Encryption function
-   * @param {BigInteger} m
-   * @param {BigInteger} p
-   * @param {BigInteger} g
-   * @param {BigInteger} y
-   * @returns {{ c1: BigInteger, c2: BigInteger }}
+   * Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)
+   * @param {Uint8Array} data to be padded and encrypted
+   * @param {Uint8Array} p
+   * @param {Uint8Array} g
+   * @param {Uint8Array} y
+   * @returns {{ c1: Uint8Array, c2: Uint8Array }}
    * @async
    */
-  encrypt: async function(m, p, g, y) {
+  encrypt: async function(data, p, g, y) {
     const BigInteger = await util.getBigInteger();
-    // See Section 11.5 here: https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf
-    const k = await random.getRandomBigInteger(new BigInteger(0), p); // returns in [0, p-1]
+    p = new BigInteger(p);
+    g = new BigInteger(g);
+    y = new BigInteger(y);
+
+    const padded = await pkcs1.eme.encode(data, p.byteLength());
+    const m = new BigInteger(padded);
+
+    // OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
+    // hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2]
+    const k = await random.getRandomBigInteger(new BigInteger(1), p.dec());
     return {
-      c1: g.modExp(k, p),
-      c2: y.modExp(k, p).imul(m).imod(p)
+      c1: g.modExp(k, p).toUint8Array(),
+      c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
     };
   },
 
   /**
    * ElGamal Encryption function
-   * @param {BigInteger} c1
-   * @param {BigInteger} c2
-   * @param {BigInteger} p
-   * @param {BigInteger} x
-   * @returns BigInteger
+   * @param {Uint8Array} c1
+   * @param {Uint8Array} c2
+   * @param {Uint8Array} p
+   * @param {Uint8Array} x
+   * @returns {Uint8Array} unpadded message
    * @async
    */
   decrypt: async function(c1, c2, p, x) {
-    return c1.modExp(x, p).modInv(p).imul(c2).imod(p);
+    const BigInteger = await util.getBigInteger();
+    c1 = new BigInteger(c1);
+    c2 = new BigInteger(c2);
+    p = new BigInteger(p);
+    x = new BigInteger(x);
+
+    const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
+    return pkcs1.eme.decode(padded.toUint8Array('be', p.byteLength()));
   },
 
   /**
diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js
index 40fc2cc4..f99c9a6a 100644
--- a/src/crypto/public_key/elliptic/curves.js
+++ b/src/crypto/public_key/elliptic/curves.js
@@ -214,10 +214,12 @@ async function generate(curve) {
 
   curve = new Curve(curve);
   const keyPair = await curve.genKeyPair();
+  const Q = new BigInteger(keyPair.publicKey).toUint8Array();
+  const secret = new BigInteger(keyPair.privateKey).toUint8Array('be', curve.payloadSize);
   return {
     oid: curve.oid,
-    Q: new BigInteger(keyPair.publicKey),
-    d: new BigInteger(keyPair.privateKey),
+    Q,
+    secret,
     hash: curve.hash,
     cipher: curve.cipher
   };
diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js
index 990d318d..3f4a2be0 100644
--- a/src/crypto/public_key/elliptic/ecdh.js
+++ b/src/crypto/public_key/elliptic/ecdh.js
@@ -37,6 +37,8 @@ import random from '../../random';
 import hash from '../../hash';
 import enums from '../../../enums';
 import util from '../../../util';
+import pkcs5 from '../../pkcs5';
+import MPI from '../../../type/mpi';
 import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
 
 const webCrypto = util.getWebCrypto();
@@ -65,31 +67,6 @@ function buildEcdhParam(public_algo, oid, kdfParams, fingerprint) {
   ]);
 }
 
-/**
- * Parses MPI params and returns them as byte arrays of fixed length
- * @param {Array} params key parameters
- * @returns {Object} parameters in the form
- *  { oid, kdfParams, d: Uint8Array, Q: Uint8Array }
- */
-function parseParams(params) {
-  if (params.length < 3 || params.length > 4) {
-    throw new Error('Unexpected number of parameters');
-  }
-
-  const oid = params[0];
-  const curve = new Curve(oid);
-  const parsedParams = { oid };
-  // The public point never has leading zeros, as it is prefixed by 0x40 or 0x04
-  parsedParams.Q = params[1].toUint8Array();
-  parsedParams.kdfParams = params[2];
-
-  if (params.length === 4) {
-    parsedParams.d = params[3].toUint8Array('be', curve.payloadSize);
-  }
-
-  return parsedParams;
-}
-
 // Key Derivation Function (RFC 6637)
 async function kdf(hash_algo, X, length, param, stripLeading = false, stripTrailing = false) {
   // Note: X is little endian for Curve25519, big-endian for all others.
@@ -151,13 +128,15 @@ async function genPublicEphemeralKey(curve, Q) {
  *
  * @param  {module:type/oid}        oid          Elliptic curve object identifier
  * @param  {module:type/kdf_params} kdfParams    KDF params including cipher and algorithm to use
- * @param  {module:type/mpi}        m            Value derived from session key (RFC 6637)
+ * @param  {Uint8Array}             data         Unpadded session key data
  * @param  {Uint8Array}             Q            Recipient public key
  * @param  {Uint8Array}             fingerprint  Recipient fingerprint
  * @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
  * @async
  */
-async function encrypt(oid, kdfParams, m, Q, fingerprint) {
+async function encrypt(oid, kdfParams, data, Q, fingerprint) {
+  const m = new MPI(pkcs5.encode(data));
+
   const curve = new Curve(oid);
   const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q);
   const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
@@ -214,12 +193,10 @@ async function genPrivateEphemeralKey(curve, V, Q, d) {
  * @param  {Uint8Array}             Q            Recipient public key
  * @param  {Uint8Array}             d            Recipient private key
  * @param  {Uint8Array}             fingerprint  Recipient fingerprint
- * @returns {Promise<BigInteger>}                        Value derived from session key
+ * @returns {Promise<Uint8Array>}   Value derived from session key
  * @async
  */
 async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
-  const BigInteger = await util.getBigInteger();
-
   const curve = new Curve(oid);
   const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
   const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
@@ -229,7 +206,7 @@ async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
     try {
       // Work around old go crypto bug and old OpenPGP.js bug, respectively.
       const Z = await kdf(kdfParams.hash, sharedKey, cipher[cipher_algo].keySize, param, i === 1, i === 2);
-      return new BigInteger(aes_kw.unwrap(Z, C));
+      return pkcs5.decode(aes_kw.unwrap(Z, C));
     } catch (e) {
       err = e;
     }
@@ -411,4 +388,4 @@ async function nodePublicEphemeralKey(curve, Q) {
   return { publicKey, sharedKey };
 }
 
-export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, webPrivateEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, nodePublicEphemeralKey, nodePrivateEphemeralKey, validateParams, parseParams };
+export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, webPrivateEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, nodePublicEphemeralKey, nodePrivateEphemeralKey, validateParams };
diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js
index 845450d9..6efeebc8 100644
--- a/src/crypto/public_key/elliptic/ecdsa.js
+++ b/src/crypto/public_key/elliptic/ecdsa.js
@@ -152,31 +152,7 @@ async function validateParams(oid, Q, d) {
   }
 }
 
-/**
- * Parses MPI params and returns them as byte arrays of fixed length
- * @param {Array} params key parameters
- * @returns {Object} parameters in the form
- *  { oid, d: Uint8Array, Q: Uint8Array }
- */
-function parseParams(params) {
-  if (params.length < 2 || params.length > 3) {
-    throw new Error('Unexpected number of parameters');
-  }
-
-  const oid = params[0];
-  const curve = new Curve(oid);
-  const parsedParams = { oid };
-  // The public point never has leading zeros, as it is prefixed by 0x40 or 0x04
-  parsedParams.Q = params[1].toUint8Array();
-  if (params.length === 3) {
-    parsedParams.d = params[2].toUint8Array('be', curve.payloadSize);
-  }
-
-  return parsedParams;
-}
-
-
-export default { sign, verify, ellipticVerify, ellipticSign, validateParams, parseParams };
+export default { sign, verify, ellipticVerify, ellipticSign, validateParams };
 
 
 //////////////////////////
diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js
index 7dc158ec..01cde9ee 100644
--- a/src/crypto/public_key/elliptic/eddsa.js
+++ b/src/crypto/public_key/elliptic/eddsa.js
@@ -92,28 +92,4 @@ async function validateParams(oid, Q, k) {
   return util.equalsUint8Array(Q, dG);
 }
 
-/**
- * Parses MPI params and returns them as byte arrays of fixed length
- * @param {Array} params key parameters
- * @returns {Object} parameters in the form
- *  { oid, seed: Uint8Array, Q: Uint8Array }
- */
-function parseParams(params) {
-  if (params.length < 2 || params.length > 3) {
-    throw new Error('Unexpected number of parameters');
-  }
-
-  const parsedParams = {
-    oid: params[0],
-    Q: params[1].toUint8Array('be', 33)
-  };
-
-  if (params.length === 3) {
-    parsedParams.seed = params[2].toUint8Array('be', 32);
-  }
-
-  return parsedParams;
-}
-
-
-export default { sign, verify, validateParams, parseParams };
+export default { sign, verify, validateParams };
diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js
index b3b10790..daa55cf9 100644
--- a/src/crypto/public_key/rsa.js
+++ b/src/crypto/public_key/rsa.js
@@ -171,14 +171,13 @@ export default {
    * @param {Integer} bits RSA bit length
    * @param {Integer} e    RSA public exponent
    * @returns {{n, e, d,
-   *            p, q ,u: BigInteger}} RSA public modulus, RSA public exponent, RSA private exponent,
-   *                                  RSA private prime p, RSA private prime q, u = q ** 1 mod p
+   *            p, q ,u: Uint8Array}} RSA public modulus, RSA public exponent, RSA private exponent,
+   *                                  RSA private prime p, RSA private prime q, u = p ** -1 mod q
    * @async
    */
   generate: async function(bits, e) {
     const BigInteger = await util.getBigInteger();
 
-    let key;
     e = new BigInteger(e);
 
     // Native RSA keygen using Web Crypto
@@ -221,17 +220,17 @@ export default {
       if (jwk instanceof ArrayBuffer) {
         jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
       }
-      // map JWK parameters to BN
-      key = {};
-      key.n = new BigInteger(util.b64ToUint8Array(jwk.n));
-      key.e = e;
-      key.d = new BigInteger(util.b64ToUint8Array(jwk.d));
-      // switch p and q
-      key.p = new BigInteger(util.b64ToUint8Array(jwk.q));
-      key.q = new BigInteger(util.b64ToUint8Array(jwk.p));
-      // Since p and q are switched in places, we could keep u
-      key.u = new BigInteger(util.b64ToUint8Array(jwk.qi));
-      return key;
+      // map JWK parameters to corresponding OpenPGP names
+      return {
+        n: util.b64ToUint8Array(jwk.n),
+        e: e.toUint8Array(),
+        d: util.b64ToUint8Array(jwk.d),
+        // switch p and q
+        p: util.b64ToUint8Array(jwk.q),
+        q: util.b64ToUint8Array(jwk.p),
+        // Since p and q are switched in places, u is the inverse of jwk.q
+        u: util.b64ToUint8Array(jwk.qi)
+      };
     } else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
       const opts = {
         modulusLength: bits,
@@ -246,19 +245,20 @@ export default {
           resolve(RSAPrivateKey.decode(der, 'der'));
         }
       }));
-      /**  PGP spec differs from DER spec, DER: `(inverse of q) mod p`, PGP: `(inverse of p) mod q`.
+      /**
+       * OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`.
        * @link https://tools.ietf.org/html/rfc3447#section-3.2
        * @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
        */
       return {
-        n: prv.modulus,
-        e: prv.publicExponent,
-        d: prv.privateExponent,
+        n: prv.modulus.toArrayLike(Uint8Array),
+        e: prv.publicExponent.toArrayLike(Uint8Array),
+        d: prv.privateExponent.toArrayLike(Uint8Array),
         // switch p and q
-        p: prv.prime2,
-        q: prv.prime1,
-        // Since p and q are switched in places, we could keep u
-        u: prv.coefficient // PGP type of u
+        p: prv.prime2.toArrayLike(Uint8Array),
+        q: prv.prime1.toArrayLike(Uint8Array),
+        // Since p and q are switched in places, we can keep u as defined by DER
+        u: prv.coefficient.toArrayLike(Uint8Array)
       };
     }
 
@@ -271,17 +271,16 @@ export default {
     if (q.lt(p)) {
       [p, q] = [q, p];
     }
-
     const phi = p.dec().imul(q.dec());
     return {
-      n: p.mul(q),
-      e,
-      d: e.modInv(phi),
-      p: p,
-      q: q,
+      n: p.mul(q).toUint8Array(),
+      e: e.toUint8Array(),
+      d: e.modInv(phi).toUint8Array(),
+      p: p.toUint8Array(),
+      q: q.toUint8Array(),
       // dp: d.mod(p.subn(1)),
       // dq: d.mod(q.subn(1)),
-      u: p.modInv(q)
+      u: p.modInv(q).toUint8Array()
     };
   },
 
diff --git a/src/crypto/signature.js b/src/crypto/signature.js
index eb8f763c..1681c1a2 100644
--- a/src/crypto/signature.js
+++ b/src/crypto/signature.js
@@ -18,45 +18,37 @@ export default {
    * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
    * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
    * for public key and hash algorithms.
-   * @param {module:enums.publicKey} algo      Public key algorithm
-   * @param {module:enums.hash}      hash_algo Hash algorithm
-   * @param {Array<module:type/mpi>} msg_MPIs  Algorithm-specific signature parameters
-   * @param {Array<module:type/mpi>} pub_MPIs  Algorithm-specific public key parameters
-   * @param {Uint8Array}             data      Data for which the signature was created
-   * @param {Uint8Array}             hashed    The hashed data
-   * @returns {Boolean}                        True if signature is valid
+   * @param {module:enums.publicKey} algo             Public key algorithm
+   * @param {module:enums.hash}      hash_algo        Hash algorithm
+   * @param {Array<module:type/mpi>} msg_MPIs         Algorithm-specific signature parameters
+   * @param {Object}                 publicParams  Algorithm-specific public key parameters
+   * @param {Uint8Array}             data             Data for which the signature was created
+   * @param {Uint8Array}             hashed           The hashed data
+   * @returns {Boolean}                               True if signature is valid
    * @async
    */
-  verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data, hashed) {
-    const types = crypto.getPubKeyParamTypes(algo);
-    if (pub_MPIs.length < types.length) {
-      throw new Error('Missing public key parameters');
-    }
+  verify: async function(algo, hash_algo, msg_MPIs, publicParams, data, hashed) {
     switch (algo) {
       case enums.publicKey.rsaEncryptSign:
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaSign: {
-        const n = pub_MPIs[0].toUint8Array();
-        const e = pub_MPIs[1].toUint8Array();
+        const { n, e } = publicParams;
         const m = msg_MPIs[0].toUint8Array('be', n.length);
         return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
       }
       case enums.publicKey.dsa: {
-        const r = await msg_MPIs[0].toBigInteger();
-        const s = await msg_MPIs[1].toBigInteger();
-        const p = await pub_MPIs[0].toBigInteger();
-        const q = await pub_MPIs[1].toBigInteger();
-        const g = await pub_MPIs[2].toBigInteger();
-        const y = await pub_MPIs[3].toBigInteger();
+        const r = await msg_MPIs[0].toUint8Array();
+        const s = await msg_MPIs[1].toUint8Array();
+        const { g, p, q, y } = publicParams;
         return publicKey.dsa.verify(hash_algo, r, s, hashed, g, p, q, y);
       }
       case enums.publicKey.ecdsa: {
-        const { oid, Q } = publicKey.elliptic.ecdsa.parseParams(pub_MPIs);
+        const { oid, Q } = publicParams;
         const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
         return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q, hashed);
       }
       case enums.publicKey.eddsa: {
-        const { oid, Q } = publicKey.elliptic.eddsa.parseParams(pub_MPIs);
+        const { oid, Q } = publicParams;
         // EdDSA signature params are expected in little-endian format
         const signature = {
           R: msg_MPIs[0].toUint8Array('le', 32),
@@ -74,37 +66,31 @@ export default {
    * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
    * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
    * for public key and hash algorithms.
-   * @param {module:enums.publicKey} algo       Public key algorithm
-   * @param {module:enums.hash}      hash_algo  Hash algorithm
-   * @param {Array<module:type/mpi>} key_params Algorithm-specific public and private key parameters
-   * @param {Uint8Array}             data       Data to be signed
-   * @param {Uint8Array}             hashed     The hashed data
-   * @returns {Uint8Array}                      Signature
+   * @param {module:enums.publicKey} algo             Public key algorithm
+   * @param {module:enums.hash}      hash_algo        Hash algorithm
+   * @param {Object}                 publicKeyParams  Algorithm-specific public and private key parameters
+   * @param {Object}                 privateKeyParams Algorithm-specific public and private key parameters
+   * @param {Uint8Array}             data             Data to be signed
+   * @param {Uint8Array}             hashed           The hashed data
+   * @returns {Uint8Array} Signature
    * @async
    */
-  sign: async function(algo, hash_algo, key_params, data, hashed) {
-    const types = [].concat(crypto.getPubKeyParamTypes(algo), crypto.getPrivKeyParamTypes(algo));
-    if (key_params.length < types.length) {
-      throw new Error('Missing private key parameters');
+  sign: async function(algo, hash_algo, publicKeyParams, privateKeyParams, data, hashed) {
+    if (!publicKeyParams || !privateKeyParams) {
+      throw new Error('Missing key parameters');
     }
     switch (algo) {
       case enums.publicKey.rsaEncryptSign:
       case enums.publicKey.rsaEncrypt:
       case enums.publicKey.rsaSign: {
-        const n = key_params[0].toUint8Array();
-        const e = key_params[1].toUint8Array();
-        const d = key_params[2].toUint8Array();
-        const p = key_params[3].toUint8Array();
-        const q = key_params[4].toUint8Array();
-        const u = key_params[5].toUint8Array();
+        const { n, e } = publicKeyParams;
+        const { d, p, q, u } = privateKeyParams;
         const signature = await publicKey.rsa.sign(hash_algo, data, n, e, d, p, q, u, hashed);
         return util.uint8ArrayToMpi(signature);
       }
       case enums.publicKey.dsa: {
-        const p = await key_params[0].toBigInteger();
-        const q = await key_params[1].toBigInteger();
-        const g = await key_params[2].toBigInteger();
-        const x = await key_params[4].toBigInteger();
+        const { g, p, q } = publicKeyParams;
+        const { x } = privateKeyParams;
         const signature = await publicKey.dsa.sign(hash_algo, hashed, g, p, q, x);
         return util.concatUint8Array([
           util.uint8ArrayToMpi(signature.r),
@@ -115,7 +101,8 @@ export default {
         throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
       }
       case enums.publicKey.ecdsa: {
-        const { oid, Q, d } = publicKey.elliptic.ecdsa.parseParams(key_params);
+        const { oid, Q } = publicKeyParams;
+        const { d } = privateKeyParams;
         const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, Q, d, hashed);
         return util.concatUint8Array([
           util.uint8ArrayToMpi(signature.r),
@@ -123,7 +110,8 @@ export default {
         ]);
       }
       case enums.publicKey.eddsa: {
-        const { oid, Q, seed } = publicKey.elliptic.eddsa.parseParams(key_params);
+        const { oid, Q } = publicKeyParams;
+        const { seed } = privateKeyParams;
         const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, Q, seed, hashed);
         return util.concatUint8Array([
           util.uint8ArrayToMpi(signature.R),
diff --git a/src/key/helper.js b/src/key/helper.js
index 640fcb2f..6fa49059 100644
--- a/src/key/helper.js
+++ b/src/key/helper.js
@@ -151,7 +151,7 @@ export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), us
         case 'ecdh':
         case 'ecdsa':
         case 'eddsa':
-          pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(keyPacket.params[0]);
+          pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(keyPacket.publicParams.oid);
       }
   }
   return crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?
diff --git a/src/packet/public_key.js b/src/packet/public_key.js
index 0a8e7faf..8d6542ca 100644
--- a/src/packet/public_key.js
+++ b/src/packet/public_key.js
@@ -29,7 +29,6 @@
 import { Sha1 } from 'asmcrypto.js/dist_es8/hash/sha1/sha1';
 import { Sha256 } from 'asmcrypto.js/dist_es8/hash/sha256/sha256';
 import type_keyid from '../type/keyid';
-import type_mpi from '../type/mpi';
 import config from '../config';
 import crypto from '../crypto';
 import enums from '../enums';
@@ -70,10 +69,10 @@ class PublicKeyPacket {
      */
     this.algorithm = null;
     /**
-     * Algorithm specific params
-     * @type {Array<Object>}
+     * Algorithm specific public params
+     * @type {Object}
      */
-    this.params = [];
+    this.publicParams = null;
     /**
      * Time until expiration in days (V3 only)
      * @type {Integer}
@@ -116,16 +115,13 @@ class PublicKeyPacket {
         pos += 4;
       }
 
-      // - A series of values comprising the key material.  This is
-      //   algorithm-specific and described in section XXXX.
-      const types = crypto.getPubKeyParamTypes(algo);
-      this.params = crypto.constructParams(types);
-
-      for (let i = 0; i < types.length && pos < bytes.length; i++) {
-        pos += this.params[i].read(bytes.subarray(pos, bytes.length));
-        if (pos > bytes.length) {
-          throw new Error('Error reading MPI @:' + pos);
-        }
+      // - A series of values comprising the key material.
+      try {
+        const { read, publicParams } = crypto.parsePublicKeyParams(algo, bytes.subarray(pos));
+        this.publicParams = publicParams;
+        pos += read;
+      } catch (err) {
+        throw new Error('Error reading MPIs');
       }
 
       return pos;
@@ -134,9 +130,8 @@ class PublicKeyPacket {
   }
 
   /**
-   * Same as write_private_key, but has less information because of
-   * public key.
-   * @returns {Uint8Array} OpenPGP packet body contents,
+   * Creates an OpenPGP public key packet for the given key.
+   * @returns {Uint8Array} Bytes encoding the public key OpenPGP packet
    */
   write() {
     const arr = [];
@@ -147,8 +142,7 @@ class PublicKeyPacket {
     const algo = enums.write(enums.publicKey, this.algorithm);
     arr.push(new Uint8Array([algo]));
 
-    const paramCount = crypto.getPubKeyParamTypes(algo).length;
-    const params = util.concatUint8Array(this.params.slice(0, paramCount).map(param => param.write()));
+    const params = crypto.serializeKeyParams(algo, this.publicParams);
     if (this.version === 5) {
       // A four-octet scalar octet count for the following key material
       arr.push(util.writeNumber(params.length, 4));
@@ -243,11 +237,11 @@ class PublicKeyPacket {
   getAlgorithmInfo() {
     const result = {};
     result.algorithm = this.algorithm;
-    if (this.params[0] instanceof type_mpi) {
-      result.rsaBits = this.params[0].byteLength() * 8;
+    if (this.publicParams.n) {
+      result.rsaBits = this.publicParams.n.length * 8;
       result.bits = result.rsaBits; // Deprecated.
     } else {
-      result.curve = this.params[0].getName();
+      result.curve = this.publicParams.oid.getName();
     }
     return result;
   }
diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js
index 519816e6..ddc29615 100644
--- a/src/packet/public_key_encrypted_session_key.js
+++ b/src/packet/public_key_encrypted_session_key.js
@@ -110,7 +110,7 @@ class PublicKeyEncryptedSessionKeyPacket {
     ]);
     const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
     this.encrypted = await crypto.publicKeyEncrypt(
-      algo, key.params, data, key.getFingerprintBytes());
+      algo, key.publicParams, data, key.getFingerprintBytes());
     return true;
   }
 
@@ -130,7 +130,7 @@ class PublicKeyEncryptedSessionKeyPacket {
     if (algo !== keyAlgo) {
       throw new Error('Decryption error');
     }
-    const decoded = await crypto.publicKeyDecrypt(algo, key.params, this.encrypted, key.getFingerprintBytes());
+    const decoded = await crypto.publicKeyDecrypt(algo, key.publicParams, key.privateParams, this.encrypted, key.getFingerprintBytes());
     const checksum = decoded.subarray(decoded.length - 2);
     const sessionKey = decoded.subarray(1, decoded.length - 2);
     if (!util.equalsUint8Array(checksum, util.writeChecksum(sessionKey))) {
diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js
index 6b283f8c..8ccd3dbe 100644
--- a/src/packet/secret_key.js
+++ b/src/packet/secret_key.js
@@ -73,6 +73,11 @@ class SecretKeyPacket extends PublicKeyPacket {
      * @type {String}
      */
     this.aead = null;
+    /**
+     * Decrypted private parameters, referenced by name
+     * @type {Object}
+     */
+    this.privateParams = null;
   }
 
   // 5.5.3.  Secret-Key Packet Formats
@@ -154,8 +159,13 @@ class SecretKeyPacket extends PublicKeyPacket {
       if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) {
         throw new Error('Key checksum mismatch');
       }
-      const privParams = parse_cleartext_params(cleartext, this.algorithm);
-      this.params = this.params.concat(privParams);
+      try {
+        const algo = enums.write(enums.publicKey, this.algorithm);
+        const { privateParams } = crypto.parsePrivateKeyParams(algo, cleartext, this.publicParams);
+        this.privateParams = privateParams;
+      } catch (err) {
+        throw new Error('Error reading MPIs');
+      }
     }
   }
 
@@ -200,7 +210,8 @@ class SecretKeyPacket extends PublicKeyPacket {
 
     if (!this.isDummy()) {
       if (!this.s2k_usage) {
-        const cleartextParams = write_cleartext_params(this.params, this.algorithm);
+        const algo = enums.write(enums.publicKey, this.algorithm);
+        const cleartextParams = crypto.serializeKeyParams(algo, this.privateParams);
         this.keyMaterial = util.concatUint8Array([
           cleartextParams,
           util.writeChecksum(cleartextParams)
@@ -282,7 +293,8 @@ class SecretKeyPacket extends PublicKeyPacket {
 
     this.s2k = new type_s2k();
     this.s2k.salt = await crypto.random.getRandomBytes(8);
-    const cleartext = write_cleartext_params(this.params, this.algorithm);
+    const algo = enums.write(enums.publicKey, this.algorithm);
+    const cleartext = crypto.serializeKeyParams(algo, this.privateParams);
     this.symmetric = 'aes256';
     const key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric);
     const blockLen = crypto.cipher[this.symmetric].blockSize;
@@ -354,8 +366,13 @@ class SecretKeyPacket extends PublicKeyPacket {
       }
     }
 
-    const privParams = parse_cleartext_params(cleartext, this.algorithm);
-    this.params = this.params.concat(privParams);
+    try {
+      const algo = enums.write(enums.publicKey, this.algorithm);
+      const { privateParams } = crypto.parsePrivateKeyParams(algo, cleartext, this.publicParams);
+      this.privateParams = privateParams;
+    } catch (err) {
+      throw new Error('Error reading MPIs');
+    }
     this.isEncrypted = false;
     this.keyMaterial = null;
     this.s2k_usage = 0;
@@ -378,7 +395,14 @@ class SecretKeyPacket extends PublicKeyPacket {
     }
 
     const algo = enums.write(enums.publicKey, this.algorithm);
-    const validParams = await crypto.validateParams(algo, this.params);
+
+    let validParams;
+    try {
+      // this can throw if some parameters are undefined
+      validParams = await crypto.validateParams(algo, this.publicParams, this.privateParams);
+    } catch (_) {
+      validParams = false;
+    }
     if (!validParams) {
       throw new Error('Key is invalid');
     }
@@ -387,7 +411,9 @@ class SecretKeyPacket extends PublicKeyPacket {
 
   async generate(bits, curve) {
     const algo = enums.write(enums.publicKey, this.algorithm);
-    this.params = await crypto.generateParams(algo, bits, curve);
+    const { privateParams, publicParams } = await crypto.generateParams(algo, bits, curve);
+    this.privateParams = privateParams;
+    this.publicParams = publicParams;
     this.isEncrypted = false;
   }
 
@@ -400,47 +426,16 @@ class SecretKeyPacket extends PublicKeyPacket {
       return;
     }
 
-    const algo = enums.write(enums.publicKey, this.algorithm);
-    const publicParamCount = crypto.getPubKeyParamTypes(algo).length;
-    this.params.slice(publicParamCount).forEach(param => {
-      param.data.fill(0);
+    Object.keys(this.privateParams).forEach(name => {
+      const param = this.privateParams[name];
+      param.fill(0);
+      delete this.privateParams[name];
     });
-    this.params.length = publicParamCount;
+    this.privateParams = null;
     this.isEncrypted = true;
   }
 }
 
-// Helper function
-
-function parse_cleartext_params(cleartext, algorithm) {
-  const algo = enums.write(enums.publicKey, algorithm);
-  const types = crypto.getPrivKeyParamTypes(algo);
-  const params = crypto.constructParams(types);
-  let p = 0;
-
-  for (let i = 0; i < types.length && p < cleartext.length; i++) {
-    p += params[i].read(cleartext.subarray(p, cleartext.length));
-    if (p > cleartext.length) {
-      throw new Error('Error reading param @:' + p);
-    }
-  }
-
-  return params;
-}
-
-function write_cleartext_params(params, algorithm) {
-  const arr = [];
-  const algo = enums.write(enums.publicKey, algorithm);
-  const numPublicParams = crypto.getPubKeyParamTypes(algo).length;
-
-  for (let i = numPublicParams; i < params.length; i++) {
-    arr.push(params[i].write());
-  }
-
-  return util.concatUint8Array(arr);
-}
-
-
 async function produceEncryptionKey(s2k, passphrase, algorithm) {
   return s2k.produce_key(
     passphrase,
diff --git a/src/packet/signature.js b/src/packet/signature.js
index 68196cc9..7ca56314 100644
--- a/src/packet/signature.js
+++ b/src/packet/signature.js
@@ -177,9 +177,8 @@ class SignaturePacket {
     const hash = await this.hash(signatureType, data, toHash, detached);
 
     this.signedHashValue = stream.slice(stream.clone(hash), 0, 2);
-    const params = key.params;
     const signed = async () => crypto.signature.sign(
-      publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash)
+      publicKeyAlgorithm, hashAlgorithm, key.publicParams, key.privateParams, toHash, await stream.readToEnd(hash)
     );
     if (streaming) {
       this.signature = stream.fromAsync(signed);
@@ -714,7 +713,7 @@ class SignaturePacket {
       i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian);
     }
     const verified = await crypto.signature.verify(
-      publicKeyAlgorithm, hashAlgorithm, mpi, key.params,
+      publicKeyAlgorithm, hashAlgorithm, mpi, key.publicParams,
       toHash, hash
     );
     if (!verified) {
diff --git a/src/util.js b/src/util.js
index 33c2e229..ffb4fcfe 100644
--- a/src/util.js
+++ b/src/util.js
@@ -167,6 +167,31 @@ export default {
     return str;
   },
 
+  /**
+   * Read one MPI from bytes in input
+   * @param {Uint8Array} bytes  input data to parse
+   * @returns {Uint8Array} parsed MPI
+   */
+  readMPI: function (bytes) {
+    const bits = (bytes[0] << 8) | bytes[1];
+    const bytelen = (bits + 7) >>> 3;
+    return bytes.subarray(2, 2 + bytelen);
+  },
+
+  /**
+   * Pad Uint8Array to length by adding 0x0 bytes
+   * @param {Uint8Array} bytes      data to pad
+   * @param {Number}     length     padded length
+   * @param {'be'|'le'}  endianess  endianess of input data
+   * @return {Uint8Array} padded bytes
+   */
+  padToLength(bytes, length, endianess = 'be') {
+    const padded = new Uint8Array(length);
+    const offset = (endianess === 'be') ? 0 : (length - bytes.length);
+    padded.set(bytes, offset);
+    return padded;
+  },
+
   /**
    * Convert a Uint8Array to an MPI-formatted Uint8Array.
    * Note: the output is **not** an MPI object.
diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js
index 508a86eb..63171ba1 100644
--- a/test/crypto/crypto.js
+++ b/test/crypto/crypto.js
@@ -8,7 +8,7 @@ const expect = chai.expect;
 module.exports = () => describe('API functional testing', function() {
   const util = openpgp.util;
   const crypto = openpgp.crypto;
-  const RSApubMPIstrs = [
+  const RSAPublicKeyMaterial = util.concatUint8Array([
     new Uint8Array([0x08,0x00,0xac,0x15,0xb3,0xd6,0xd2,0x0f,0xf0,0x7a,0xdd,0x21,0xb7,
       0xbf,0x61,0xfa,0xca,0x93,0x86,0xc8,0x55,0x5a,0x4b,0xa6,0xa4,0x1a,
       0x60,0xa2,0x3a,0x37,0x06,0x08,0xd8,0x15,0x8e,0x85,0x45,0xaa,0xb7,
@@ -30,8 +30,8 @@ module.exports = () => describe('API functional testing', function() {
       0xee,0xc9,0xa4,0xcd,0x15,0xdc,0x1b,0x8d,0x64,0xc1,0x36,0x17,0xc4,
       0x8d,0x5e,0x99,0x7a,0x5b,0x9f,0x39,0xd0,0x00,0x6e,0xf9]),
     new Uint8Array([0x00,0x11,0x01,0x00,0x01])
-  ];
-  const RSAsecMPIstrs = [
+  ]);
+  const RSAPrivateKeyMaterial = util.concatUint8Array([
     new Uint8Array([0x07,0xfe,0x23,0xff,0xce,0x45,0x6c,0x60,0x65,0x40,0x6e,0xae,0x35,
       0x10,0x56,0x60,0xee,0xab,0xfa,0x10,0x42,0xba,0xc7,0x04,0xaf,0x63,
       0xcd,0x3f,0x62,0xca,0x4b,0xfa,0xe1,0xa9,0x70,0xcd,0x34,0x8b,0xc8,
@@ -82,9 +82,9 @@ module.exports = () => describe('API functional testing', function() {
       0x51,0xe0,0x22,0xf0,0xff,0xa7,0x42,0xd4,0xde,0x0b,0x47,0x8f,0x2b,
       0xf5,0x4d,0x04,0x32,0x91,0x89,0x4b,0x0e,0x05,0x8d,0x70,0xf9,0xbb,
       0xe7,0xd6,0x76,0xea,0x0e,0x1a,0x90,0x30,0xf5,0x98,0x01,0xc5,0x73])
-  ];
+  ]);
 
-  const DSApubMPIstrs = [
+  const DSAPublicKeyMaterial = util.concatUint8Array([
     new Uint8Array([0x08,0x00,0xa8,0x85,0x5c,0x28,0x05,0x94,0x03,0xbe,0x07,0x6c,0x13,0x3e,0x65,
       0xfb,0xb5,0xe1,0x99,0x7c,0xfa,0x84,0xe3,0xac,0x47,0xa5,0xc4,0x46,0xd8,0x5f,
       0x44,0xe9,0xc1,0x6b,0x69,0xf7,0x10,0x76,0x49,0xa7,0x25,0x85,0xf4,0x1b,0xed,
@@ -142,14 +142,14 @@ module.exports = () => describe('API functional testing', function() {
       0x67,0x8d,0x9d,0x14,0xb6,0x9d,0x32,0x82,0xd0,0xb5,0xc6,0x57,0xf0,0x91,0xd9,
       0xc3,0x26,0xae,0x9f,0xa9,0x67,0x49,0x96,0x5c,0x07,0x3e,0x47,0x5c,0xed,0x60,
       0x07,0xac,0x6a])
-  ];
-  const DSAsecMPIstrs = [
+  ]);
+  const DSAPrivateKeyMaterial = util.concatUint8Array([
     new Uint8Array([0x01,0x00,0x9b,0x58,0xa8,0xf4,0x04,0xb1,0xd5,0x14,0x09,0xe1,0xe1,0xa1,0x8a,
       0x0b,0xa3,0xc3,0xa3,0x66,0xaa,0x27,0x99,0x50,0x1c,0x4d,0xba,0x24,0xee,0xdf,
       0xdf,0xb8,0x8e,0x8e])
-  ];
+  ]);
 
-  const ElgamalpubMPIstrs = [
+  const elGamalPublicKeyMaterial = util.concatUint8Array([
     new Uint8Array([0x08,0x00,0xea,0xcc,0xbe,0xe2,0xe4,0x5a,0x51,0x18,0x93,0xa1,0x12,0x2f,0x00,
       0x99,0x42,0xd8,0x5c,0x1c,0x2f,0xb6,0x3c,0xd9,0x94,0x61,0xb4,0x55,0x8d,0x4e,
       0x73,0xe6,0x69,0xbc,0x1d,0x33,0xe3,0x2d,0x91,0x23,0x69,0x95,0x98,0xd7,0x18,
@@ -187,64 +187,37 @@ module.exports = () => describe('API functional testing', function() {
       0xda,0xba,0x19,0xf3,0xcb,0x10,0xa0,0x6b,0xd0,0x2d,0xbe,0x40,0x42,0x7b,0x9b,
       0x15,0xa4,0x2d,0xec,0xcf,0x09,0xd6,0xe3,0x92,0xc3,0x8d,0x65,0x6b,0x60,0x97,
       0xda,0x6b,0xca])
-  ];
+  ]);
 
-  const ElgamalsecMPIstrs = [
+  const elGamalPrivateKeyMaterial = util.concatUint8Array([
     new Uint8Array([0x01,0x52,0x02,0x80,0x87,0xf6,0xe4,0x49,0xd7,0x2e,0x3e,0xfe,0x60,0xb9,0xa3,
       0x2a,0xf0,0x67,0x58,0xe9,0xf6,0x47,0x83,0xde,0x7e,0xfb,0xbb,0xbd,0xdf,0x48,
       0x12,0x1b,0x06,0x7d,0x13,0xbc,0x3b,0x49,0xf9,0x86,0xd4,0x53,0xed,0x2d,0x68])
-  ];
+  ]);
 
-  const RSApubMPIs = [];
-  let i;
-  for (i = 0; i < 2; i++) {
-    RSApubMPIs[i] = new openpgp.MPI();
-    RSApubMPIs[i].read(RSApubMPIstrs[i]);
-  }
+  const algoRSA = openpgp.enums.publicKey.rsaEncryptSign;
+  const RSAPublicParams = crypto.parsePublicKeyParams(algoRSA, RSAPublicKeyMaterial).publicParams;
+  const RSAPrivateParams = crypto.parsePrivateKeyParams(algoRSA, RSAPrivateKeyMaterial).privateParams;
 
-  const RSAsecMPIs = [];
-  for (i = 0; i < 4; i++) {
-    RSAsecMPIs[i] = new openpgp.MPI();
-    RSAsecMPIs[i].read(RSAsecMPIstrs[i]);
-  }
+  const algoDSA = openpgp.enums.publicKey.dsa;
+  const DSAPublicParams = crypto.parsePublicKeyParams(algoDSA, DSAPublicKeyMaterial).publicParams;
+  const DSAPrivateParams = crypto.parsePrivateKeyParams(algoDSA, DSAPrivateKeyMaterial).privateParams;
 
-  const DSAsecMPIs = [];
-  for (i = 0; i < 1; i++) {
-    DSAsecMPIs[i] = new openpgp.MPI();
-    DSAsecMPIs[i].read(DSAsecMPIstrs[i]);
-  }
-
-  const DSApubMPIs = [];
-  for (i = 0; i < 4; i++) {
-    DSApubMPIs[i] = new openpgp.MPI();
-    DSApubMPIs[i].read(DSApubMPIstrs[i]);
-  }
-  const ElgamalsecMPIs = [];
-  for (i = 0; i < 1; i++) {
-    ElgamalsecMPIs[i] = new openpgp.MPI();
-    ElgamalsecMPIs[i].read(ElgamalsecMPIstrs[i]);
-  }
-
-  const ElgamalpubMPIs = [];
-  for (i = 0; i < 3; i++) {
-    ElgamalpubMPIs[i] = new openpgp.MPI();
-    ElgamalpubMPIs[i].read(ElgamalpubMPIstrs[i]);
-  }
+  const algoElGamal = openpgp.enums.publicKey.elgamal;
+  const elGamalPublicParams = crypto.parsePublicKeyParams(algoElGamal, elGamalPublicKeyMaterial).publicParams;
+  const elGamalPrivateParams = crypto.parsePrivateKeyParams(algoElGamal, elGamalPrivateKeyMaterial).privateParams;
 
   const data = util.strToUint8Array("foobar");
 
   describe('Sign and verify', function () {
     it('RSA', async function () {
-      // FIXME
-      //Originally we passed public and secret MPI separately, now they are joined. Is this what we want to do long term?
-      // RSA
       return crypto.signature.sign(
-        1, 2, RSApubMPIs.concat(RSAsecMPIs), data, await crypto.hash.digest(2, data)
+        1, 2, RSAPublicParams, RSAPrivateParams, data, await crypto.hash.digest(2, data)
       ).then(async RSAsignedData => {
         const RSAsignedDataMPI = new openpgp.MPI();
         RSAsignedDataMPI.read(RSAsignedData);
         return crypto.signature.verify(
-          1, 2, [RSAsignedDataMPI], RSApubMPIs, data, await crypto.hash.digest(2, data)
+          1, 2, [RSAsignedDataMPI], RSAPublicParams, data, await crypto.hash.digest(2, data)
         ).then(success => {
           return expect(success).to.be.true;
         });
@@ -252,9 +225,8 @@ module.exports = () => describe('API functional testing', function() {
     });
 
     it('DSA', async function () {
-      // DSA
       return crypto.signature.sign(
-        17, 2, DSApubMPIs.concat(DSAsecMPIs), data, await crypto.hash.digest(2, data)
+        17, 2, DSAPublicParams, DSAPrivateParams, data, await crypto.hash.digest(2, data)
       ).then(async DSAsignedData => {
         DSAsignedData = util.uint8ArrayToStr(DSAsignedData);
         const DSAmsgMPIs = [];
@@ -263,7 +235,7 @@ module.exports = () => describe('API functional testing', function() {
         DSAmsgMPIs[0].read(DSAsignedData.substring(0,34));
         DSAmsgMPIs[1].read(DSAsignedData.substring(34,68));
         return crypto.signature.verify(
-          17, 2, DSAmsgMPIs, DSApubMPIs, data, await crypto.hash.digest(2, data)
+          17, 2, DSAmsgMPIs, DSAPublicParams, data, await crypto.hash.digest(2, data)
         ).then(success => {
           return expect(success).to.be.true;
         });
@@ -356,9 +328,9 @@ module.exports = () => describe('API functional testing', function() {
 
     it('Asymmetric using RSA with eme_pkcs1 padding', async function () {
       const symmKey = await crypto.generateSessionKey('aes256');
-      return crypto.publicKeyEncrypt(1, RSApubMPIs, symmKey).then(RSAEncryptedData => {
+      return crypto.publicKeyEncrypt(algoRSA, RSAPublicParams, symmKey).then(RSAEncryptedData => {
         return crypto.publicKeyDecrypt(
-          1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
+          algoRSA, RSAPublicParams, RSAPrivateParams, RSAEncryptedData
         ).then(data => {
           expect(data).to.deep.equal(symmKey);
         });
@@ -367,9 +339,9 @@ module.exports = () => describe('API functional testing', function() {
 
     it('Asymmetric using Elgamal with eme_pkcs1 padding', async function () {
       const symmKey = await crypto.generateSessionKey('aes256');
-      return crypto.publicKeyEncrypt(16, ElgamalpubMPIs, symmKey).then(ElgamalEncryptedData => {
+      return crypto.publicKeyEncrypt(algoElGamal, elGamalPublicParams, symmKey).then(ElgamalEncryptedData => {
         return crypto.publicKeyDecrypt(
-          16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData
+          algoElGamal, elGamalPublicParams, elGamalPrivateParams, ElgamalEncryptedData
         ).then(data => {
           expect(data).to.deep.equal(symmKey);
         });
diff --git a/test/crypto/rsa.js b/test/crypto/rsa.js
index 51962e15..672c889f 100644
--- a/test/crypto/rsa.js
+++ b/test/crypto/rsa.js
@@ -22,16 +22,11 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
 
   it('sign and verify using generated key params', async function() {
     const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
     const message = await openpgp.crypto.random.getRandomBytes(64);
     const hash_algo = openpgp.enums.write(openpgp.enums.hash, 'sha256');
     const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const signature = await openpgp.crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
     expect(signature).to.exist;
     const verify = await openpgp.crypto.publicKey.rsa.verify(hash_algo, message, signature, n, e, hashed);
@@ -40,13 +35,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
 
   it('encrypt and decrypt using generated key params', async function() {
     const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const message = await openpgp.crypto.generateSessionKey('aes256');
     const encrypted = await openpgp.crypto.publicKey.rsa.encrypt(message, n, e);
     const result = new openpgp.MPI(encrypted);
@@ -59,13 +49,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
       this.skip();
     }
     const bits = 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const message = await openpgp.crypto.generateSessionKey('aes256');
     const encryptedBn = await openpgp.crypto.publicKey.rsa.bnEncrypt(message, n, e);
     const decrypted1 = await openpgp.crypto.publicKey.rsa.nodeDecrypt(encryptedBn, n, e, d, p, q, u);
@@ -80,13 +65,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
       this.skip();
     }
     const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const message = await openpgp.crypto.random.getRandomBytes(64);
     const hashName = 'sha256';
     const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
@@ -107,13 +87,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
       this.skip();
     }
     const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const message = await openpgp.crypto.random.getRandomBytes(64);
     const hashName = 'sha256';
     const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
@@ -137,13 +112,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
       this.skip();
     }
     const bits = 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const message = await openpgp.crypto.random.getRandomBytes(64);
     const hashName = 'sha256';
     const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
@@ -158,13 +128,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
       this.skip();
     }
     const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
-    const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
-    const n = keyParams[0].toUint8Array();
-    const e = keyParams[1].toUint8Array();
-    const d = keyParams[2].toUint8Array();
-    const p = keyParams[3].toUint8Array();
-    const q = keyParams[4].toUint8Array();
-    const u = keyParams[5].toUint8Array();
+    const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
+    const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
     const message = await openpgp.crypto.random.getRandomBytes(64);
     const hashName = 'sha256';
     const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
diff --git a/test/crypto/validate.js b/test/crypto/validate.js
index b19f1766..84c24c1b 100644
--- a/test/crypto/validate.js
+++ b/test/crypto/validate.js
@@ -93,12 +93,12 @@ module.exports = () => {
 
     it('detect invalid edDSA Q', async function() {
       const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
-      const Q = eddsaKeyPacket.params[1];
-      Q.data[0]++;
+      const Q = eddsaKeyPacket.publicParams.Q;
+      Q[0]++;
       await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      const infQ = new Uint8Array(Q.data.length);
-      eddsaKeyPacket.params[1] = new openpgp.MPI(infQ);
+      const infQ = new Uint8Array(Q.length);
+      eddsaKeyPacket.publicParams.Q = infQ;
       await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
   });
@@ -114,86 +114,78 @@ module.exports = () => {
     });
 
     it('EdDSA params are not valid for ECDH', async function() {
-      const oid = eddsaKey.keyPacket.params[0];
-      const Q = eddsaKey.keyPacket.params[1];
-      const seed = eddsaKey.keyPacket.params[2];
+      const { oid, Q } = eddsaKey.keyPacket.publicParams;
+      const { seed } = eddsaKey.keyPacket.privateParams;
 
       const ecdhKeyPacket = cloneKeyPacket(ecdhKey);
-      const ecdhOID = ecdhKeyPacket.params[0];
+      const ecdhOID = ecdhKeyPacket.publicParams.oid;
 
-      ecdhKeyPacket.params[0] = oid;
+      ecdhKeyPacket.publicParams.oid = oid;
       await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      ecdhKeyPacket.params[0] = ecdhOID;
-      ecdhKeyPacket.params[1] = Q;
-      ecdhKeyPacket.params[3] = seed;
+      ecdhKeyPacket.publicParams.oid = ecdhOID;
+      ecdhKeyPacket.publicParams.Q = Q;
+      ecdhKeyPacket.privateParams.d = seed;
       await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('EdDSA params are not valid for EcDSA', async function() {
-      const oid = eddsaKey.keyPacket.params[0];
-      const Q = eddsaKey.keyPacket.params[1];
-      const seed = eddsaKey.keyPacket.params[2];
+      const { oid, Q } = eddsaKey.keyPacket.publicParams;
+      const { seed } = eddsaKey.keyPacket.privateParams;
 
       const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
-      const ecdsaOID = ecdsaKeyPacket.params[0];
-      ecdsaKeyPacket.params[0] = oid;
+      const ecdsaOID = ecdsaKeyPacket.publicParams.oid;
+      ecdsaKeyPacket.publicParams.oid = oid;
       await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      ecdsaKeyPacket.params[0] = ecdsaOID;
-      ecdsaKeyPacket.params[1] = Q;
-      ecdsaKeyPacket.params[2] = seed;
+      ecdsaKeyPacket.publicParams.oid = ecdsaOID;
+      ecdsaKeyPacket.publicParams.Q = Q;
+      ecdsaKeyPacket.privateParams.d = seed;
       await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('ECDH x25519 params are not valid for EcDSA', async function() {
-      const ecdh25519KeyPacket = ecdhKey.keyPacket;
-      const oid = ecdh25519KeyPacket.params[0];
-      const Q = ecdh25519KeyPacket.params[1];
-      const d = ecdh25519KeyPacket.params[3];
+      const { oid, Q } = ecdhKey.keyPacket.publicParams;
+      const { d } = ecdhKey.keyPacket.privateParams;
 
       const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
-      ecdsaKeyPacket.params[0] = oid;
-      ecdsaKeyPacket.params[1] = Q;
-      ecdsaKeyPacket.params[2] = d;
+      ecdsaKeyPacket.publicParams.oid = oid;
+      ecdsaKeyPacket.publicParams.Q = Q;
+      ecdsaKeyPacket.privateParams.d = d;
       await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('EcDSA params are not valid for EdDSA', async function() {
-      const oid = ecdsaKey.keyPacket.params[0];
-      const Q = ecdsaKey.keyPacket.params[1];
-      const d = ecdsaKey.keyPacket.params[2];
+      const { oid, Q } = ecdsaKey.keyPacket.publicParams;
+      const { d } = ecdsaKey.keyPacket.privateParams;
 
       const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
-      const eddsaOID = eddsaKeyPacket.params[0];
-      eddsaKeyPacket.params[0] = oid;
+      const eddsaOID = eddsaKeyPacket.publicParams.oid;
+      eddsaKeyPacket.publicParams.oid = oid;
       await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      eddsaKeyPacket.params[0] = eddsaOID;
-      eddsaKeyPacket.params[1] = Q;
-      eddsaKeyPacket.params[2] = d;
+      eddsaKeyPacket.publicParams.oid = eddsaOID;
+      eddsaKeyPacket.publicParams.Q = Q;
+      eddsaKeyPacket.privateParams.seed = d;
       await expect(eddsaKeyPacket.validate()).to.be.rejected;
     });
 
     it('ECDH x25519 params are not valid for EdDSA', async function() {
-      const ecdh25519KeyPacket = ecdhKey.keyPacket;
-      const oid = ecdh25519KeyPacket.params[0];
-      const Q = ecdh25519KeyPacket.params[1];
-      const d = ecdh25519KeyPacket.params[3];
+      const { oid, Q } = ecdhKey.keyPacket.publicParams;
+      const { d } = ecdhKey.keyPacket.privateParams;
 
       const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
-      const eddsaOID = eddsaKeyPacket.params[0];
-      eddsaKeyPacket.params[0] = oid;
+      const eddsaOID = eddsaKeyPacket.publicParams.oid;
+      eddsaKeyPacket.publicParams.oid = oid;
       await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      eddsaKeyPacket.params[0] = eddsaOID;
-      eddsaKeyPacket.params[1] = Q;
-      eddsaKeyPacket.params[2] = d;
+      eddsaKeyPacket.publicParams.oid = eddsaOID;
+      eddsaKeyPacket.publicParams.Q = Q;
+      eddsaKeyPacket.privateParams.seed = d;
       await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
   });
 
-
   const curves = ['curve25519', 'p256', 'p384', 'p521', 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
   curves.forEach(curve => {
     describe(`ECC ${curve} parameter validation`, () => {
@@ -216,12 +208,12 @@ module.exports = () => {
 
         it('detect invalid EcDSA Q', async function() {
           const keyPacket = cloneKeyPacket(ecdsaKey);
-          const Q = keyPacket.params[1];
-          Q.data[0]++;
+          const Q = keyPacket.publicParams.Q;
+          Q[0]++;
           await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-          const infQ = new Uint8Array(Q.data.length);
-          keyPacket.params[1] = new openpgp.MPI(infQ);
+          const infQ = new Uint8Array(Q.length);
+          keyPacket.publicParams.Q = infQ;
           await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
         });
       }
@@ -232,12 +224,12 @@ module.exports = () => {
 
       it('detect invalid ECDH Q', async function() {
         const keyPacket = cloneKeyPacket(ecdhKey);
-        const Q = keyPacket.params[1];
-        Q.data[16]++;
+        const Q = keyPacket.publicParams.Q;
+        Q[16]++;
         await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-        const infQ = new Uint8Array(Q.data.length);
-        keyPacket.params[1] = new openpgp.MPI(infQ);
+        const infQ = new Uint8Array(Q.length);
+        keyPacket.publicParams.Q = infQ;
         await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
       });
     });
@@ -255,15 +247,15 @@ module.exports = () => {
 
     it('detect invalid RSA n', async function() {
       const keyPacket = cloneKeyPacket(rsaKey);
-      const n = keyPacket.params[0];
-      n.data[0]++;
+      const n = keyPacket.publicParams.n;
+      n[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('detect invalid RSA e', async function() {
       const keyPacket = cloneKeyPacket(rsaKey);
-      const e = keyPacket.params[1];
-      e.data[0]++;
+      const e = keyPacket.publicParams.e;
+      e[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
   });
@@ -280,28 +272,27 @@ module.exports = () => {
 
     it('detect invalid DSA p', async function() {
       const keyPacket = cloneKeyPacket(dsaKey);
-      const p = keyPacket.params[0];
-      p.data[0]++;
+      const p = keyPacket.publicParams.p;
+      p[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('detect invalid DSA y', async function() {
       const keyPacket = cloneKeyPacket(dsaKey);
-      const y = keyPacket.params[3];
+      const y = keyPacket.publicParams.y;
 
-      y.data[0]++;
+      y[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('detect invalid DSA g', async function() {
       const keyPacket = cloneKeyPacket(dsaKey);
-      const g = keyPacket.params[2];
+      const g = keyPacket.publicParams.g;
 
-      g.data[0]++;
+      g[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      const gOne = new openpgp.MPI(new Uint8Array([1]));
-      keyPacket.params[2] = gOne;
+      keyPacket.publicParams.g = new Uint8Array([1]);
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
   });
@@ -318,40 +309,39 @@ module.exports = () => {
 
     it('detect invalid p', async function() {
       const keyPacket = cloneKeyPacket(egKey);
-      const p = keyPacket.params[0];
-      p.data[0]++;
+      const p = keyPacket.publicParams.p;
+      p[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('detect invalid y', async function() {
       const keyPacket = cloneKeyPacket(egKey);
-      const y = keyPacket.params[2];
-      y.data[0]++;
+      const y = keyPacket.publicParams.y;
+      y[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('detect invalid g', async function() {
       const keyPacket = cloneKeyPacket(egKey);
-      const g = keyPacket.params[1];
+      const g = keyPacket.publicParams.g;
 
-      g.data[0]++;
+      g[0]++;
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
 
-      const gOne = new openpgp.MPI(new Uint8Array([1]));
-      keyPacket.params[1] = gOne;
+      keyPacket.publicParams.g = new Uint8Array([1]);
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
 
     it('detect g with small order', async function() {
       const keyPacket = cloneKeyPacket(egKey);
-      const p = keyPacket.params[0].toUint8Array();
-      const g = keyPacket.params[1].toUint8Array();
+      const p = keyPacket.publicParams.p;
+      const g = keyPacket.publicParams.g;
 
       const pBN = new BN(p);
       const gModP = new BN(g).toRed(new BN.red(pBN));
       // g**(p-1)/2 has order 2
       const gOrd2 = gModP.redPow(pBN.subn(1).shrn(1));
-      keyPacket.params[1] = new openpgp.MPI(gOrd2.toArrayLike(Uint8Array, 'be'));
+      keyPacket.publicParams.g = gOrd2.toArrayLike(Uint8Array, 'be');
       await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
     });
   });
diff --git a/test/general/key.js b/test/general/key.js
index 2ed28d6e..20c33ee1 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -2584,6 +2584,17 @@ function versionSpecificTests() {
 }
 
 module.exports = () => describe('Key', function() {
+  async function deepCopyKeyParams(params) {
+    const paramsCopy = {};
+    Object.keys(params).forEach(name => {
+      const param = params[name];
+      const copy = new Uint8Array(param.length);
+      copy.set(param);
+      paramsCopy[name] = copy;
+    });
+    return paramsCopy;
+  }
+
   let rsaGenStub;
   let v5KeysVal;
   let aeadProtectVal;
@@ -2594,8 +2605,9 @@ module.exports = () => describe('Key', function() {
   };
 
   beforeEach(function() {
+    // We fake the generation function to speed up the tests
     rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
-    rsaGenStub.callsFake(N => rsaGenValue[N]);
+    rsaGenStub.callsFake(async N => deepCopyKeyParams(await rsaGenValue[N]));
   });
 
   afterEach(function() {
@@ -2882,7 +2894,7 @@ module.exports = () => describe('Key', function() {
     key.primaryKey.makeDummy();
     expect(key.primaryKey.isDummy()).to.be.true;
     await key.validate();
-    await expect(openpgp.reformatKey({ privateKey: key, userIds: 'test2 <b@a.com>' })).to.be.rejectedWith(/Missing private key parameters/);
+    await expect(openpgp.reformatKey({ privateKey: key, userIds: 'test2 <b@a.com>' })).to.be.rejectedWith(/Missing key parameters/);
   });
 
   it('makeDummy() - subkeys of the converted key can still sign', async function() {
@@ -2905,25 +2917,28 @@ module.exports = () => describe('Key', function() {
     const key = await openpgp.key.readArmored(priv_key_rsa);
     await key.decrypt('hello world');
     const signingKeyPacket = key.subKeys[0].keyPacket;
-    const params = signingKeyPacket.params;
+    const privateParams = signingKeyPacket.privateParams;
     await key.clearPrivateParams();
     key.primaryKey.isEncrypted = false;
-    key.primaryKey.params = params;
+    key.primaryKey.privateParams = privateParams;
     key.subKeys[0].keyPacket.isEncrypted = false;
-    key.subKeys[0].keyPacket.params = params;
-    await expect(key.validate()).to.be.rejectedWith('Missing key parameters');
+    key.subKeys[0].keyPacket.privateParams = privateParams;
+    await expect(key.validate()).to.be.rejectedWith('Key is invalid');
   });
 
   it('clearPrivateParams() - detect that private key parameters were zeroed out', async function() {
     const key = await openpgp.key.readArmored(priv_key_rsa);
     await key.decrypt('hello world');
     const signingKeyPacket = key.subKeys[0].keyPacket;
-    const params = signingKeyPacket.params.slice();
+    const privateParams = {};
+    Object.entries(signingKeyPacket.privateParams).forEach(([name, value]) => {
+      privateParams[name] = value;
+    });
     await key.clearPrivateParams();
     key.primaryKey.isEncrypted = false;
-    key.primaryKey.params = params;
+    key.primaryKey.privateParams = privateParams;
     key.subKeys[0].keyPacket.isEncrypted = false;
-    key.subKeys[0].keyPacket.params = params;
+    key.subKeys[0].keyPacket.privateParams = privateParams;
     await expect(key.validate()).to.be.rejectedWith('Key is invalid');
   });
 
@@ -3398,9 +3413,9 @@ VYGdb3eNlV8CfoEC
       const subKey = newPrivateKey.subKeys[total];
       expect(subKey).to.exist;
       expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
-      const subkeyN = subKey.keyPacket.params[0];
-      const pkN = privateKey.primaryKey.params[0];
-      expect(subkeyN.byteLength()).to.be.equal(rsaBits ? (rsaBits / 8) : pkN.byteLength());
+      const subkeyN = subKey.keyPacket.publicParams.n;
+      const pkN = privateKey.primaryKey.publicParams.n;
+      expect(subkeyN.length).to.be.equal(rsaBits ? (rsaBits / 8) : pkN.length);
       expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
       expect(subKey.getAlgorithmInfo().rsaBits).to.be.equal(rsaBits || privateKey.getAlgorithmInfo().rsaBits);
       await subKey.verify(newPrivateKey.primaryKey);
@@ -3446,8 +3461,8 @@ VYGdb3eNlV8CfoEC
       expect(subKey1.getKeyId().toHex()).to.be.equal(subKey.getKeyId().toHex());
       expect(subKey).to.exist;
       expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
-      const subkeyOid = subKey.keyPacket.params[0];
-      const pkOid = privateKey.primaryKey.params[0];
+      const subkeyOid = subKey.keyPacket.publicParams.oid;
+      const pkOid = privateKey.primaryKey.publicParams.oid;
       expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
       expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
       await subKey.verify(privateKey.primaryKey);
@@ -3464,7 +3479,7 @@ VYGdb3eNlV8CfoEC
       const subKey = newPrivateKey.subKeys[total];
       expect(subKey).to.exist;
       expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
-      expect(subKey.keyPacket.params[0].getName()).to.be.equal(openpgp.enums.curve.curve25519);
+      expect(subKey.keyPacket.publicParams.oid.getName()).to.be.equal(openpgp.enums.curve.curve25519);
       expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdh');
       await subKey.verify(privateKey.primaryKey);
     });
@@ -3479,8 +3494,8 @@ VYGdb3eNlV8CfoEC
       const armoredKey = newPrivateKey.armor();
       newPrivateKey = await openpgp.key.readArmored(armoredKey);
       const subKey = newPrivateKey.subKeys[total];
-      const subkeyOid = subKey.keyPacket.params[0];
-      const pkOid = newPrivateKey.primaryKey.params[0];
+      const subkeyOid = subKey.keyPacket.publicParams.oid;
+      const pkOid = newPrivateKey.primaryKey.publicParams.oid;
       expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
       expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
       await subKey.verify(newPrivateKey.primaryKey);
diff --git a/test/general/openpgp.js b/test/general/openpgp.js
index 75731fc0..826cb03c 100644
--- a/test/general/openpgp.js
+++ b/test/general/openpgp.js
@@ -549,13 +549,24 @@ function withCompression(tests) {
 }
 
 module.exports = () => describe('OpenPGP.js public api tests', function() {
+  async function deepCopyKeyParams(params) {
+    const paramsCopy = {};
+    Object.keys(params).forEach(name => {
+      const param = params[name];
+      const copy = new Uint8Array(param.length);
+      copy.set(param);
+      paramsCopy[name] = copy;
+    });
+    return paramsCopy;
+  }
 
   let rsaGenStub;
-  let rsaGenValue = openpgp.crypto.publicKey.rsa.generate(openpgp.util.getWebCryptoAll() ? 2048 : 512, 65537);
+  const rsaGenValue = openpgp.crypto.publicKey.rsa.generate(openpgp.util.getWebCryptoAll() ? 2048 : 512, 65537);
 
   beforeEach(function() {
+    // We fake the generation function to speed up the tests
     rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
-    rsaGenStub.returns(rsaGenValue);
+    rsaGenStub.returns(async () => deepCopyKeyParams(await rsaGenValue()));
   });
 
   afterEach(function() {
@@ -2329,7 +2340,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
         const pubKeyDE = await openpgp.key.readArmored(pub_key_de);
         const privKeyDE = await openpgp.key.readArmored(priv_key_de);
         // corrupt the public key params
-        privKeyDE.subKeys[0].keyPacket.params[0].data[0]++;
+        privKeyDE.subKeys[0].keyPacket.publicParams.p[0]++;
         // validation will not check the decryption subkey and will succeed
         await privKeyDE.decrypt(passphrase);
         const encrypted = await openpgp.encrypt({
diff --git a/test/general/packet.js b/test/general/packet.js
index c120f471..ed082aac 100644
--- a/test/general/packet.js
+++ b/test/general/packet.js
@@ -6,6 +6,7 @@ chai.use(require('chai-as-promised'));
 
 const { expect } = chai;
 const input = require('./testInputs.js');
+const { PacketList } = require('../../dist/node/openpgp.min');
 
 function stringify(array) {
   if (openpgp.util.isStream(array)) {
@@ -315,16 +316,10 @@ module.exports = () => describe("Packet", function() {
   });
 
   it('Public key encrypted symmetric key packet', function() {
-    const rsa = openpgp.crypto.publicKey.rsa;
+    const rsa = openpgp.enums.publicKey.rsaEncryptSign;
     const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
 
-    return rsa.generate(keySize, 65537).then(function(mpiGen) {
-
-      let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
-      mpi = mpi.map(function(k) {
-        return new openpgp.MPI(k);
-      });
-
+    return openpgp.crypto.generateParams(rsa, keySize, 65537).then(function({ publicParams, privateParams }) {
       const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket();
       const msg = new openpgp.PacketList();
       const msg2 = new openpgp.PacketList();
@@ -333,14 +328,12 @@ module.exports = () => describe("Packet", function() {
       enc.publicKeyAlgorithm = 'rsaEncryptSign';
       enc.sessionKeyAlgorithm = 'aes256';
       enc.publicKeyId.bytes = '12345678';
-      return enc.encrypt({ params: mpi, getFingerprintBytes() {} }).then(async () => {
+      return enc.encrypt({ publicParams, getFingerprintBytes() {} }).then(async () => {
 
         msg.push(enc);
-
         await msg2.read(msg.write(), openpgp);
 
-        return msg2[0].decrypt({ algorithm: 'rsaEncryptSign', params: mpi, getFingerprintBytes() {} }).then(() => {
-
+        return msg2[0].decrypt({ algorithm: 'rsaEncryptSign', publicParams, privateParams, getFingerprintBytes() {} }).then(() => {
           expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey));
           expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm);
         });
@@ -841,82 +834,67 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
     expect(rawNotations[1].humanReadable).to.equal(true);
   });
 
-  it('Writing and encryption of a secret key packet.', function() {
-    const key = new openpgp.PacketList();
-    key.push(new openpgp.SecretKeyPacket());
-
-    const rsa = openpgp.crypto.publicKey.rsa;
+  it('Writing and encryption of a secret key packet (AEAD)', async function() {
+    const rsa = openpgp.enums.publicKey.rsaEncryptSign;
     const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
+    const { privateParams, publicParams } = await openpgp.crypto.generateParams(rsa, keySize, 65537);
 
-    return rsa.generate(keySize, 65537).then(async function(mpiGen) {
-      let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
-      mpi = mpi.map(function(k) {
-        return new openpgp.MPI(k);
-      });
+    const secretKeyPacket = new openpgp.SecretKeyPacket();
+    secretKeyPacket.privateParams = privateParams;
+    secretKeyPacket.publicParams = publicParams;
+    secretKeyPacket.algorithm = "rsaSign";
+    secretKeyPacket.isEncrypted = false;
+    await secretKeyPacket.encrypt('hello');
 
-      key[0].params = mpi;
-      key[0].algorithm = "rsaSign";
-      key[0].isEncrypted = false;
-      await key[0].encrypt('hello');
+    const raw = new openpgp.PacketList();
+    raw.push(secretKeyPacket);
+    const packetList = new openpgp.PacketList();
+    await packetList.read(raw.write(), openpgp);
+    const secretKeyPacket2 = packetList[0];
+    await secretKeyPacket2.decrypt('hello');
 
-      const raw = key.write();
-
-      const key2 = new openpgp.PacketList();
-      await key2.read(raw, openpgp);
-      await key2[0].decrypt('hello');
-
-      expect(key[0].params.toString()).to.equal(key2[0].params.toString());
-    });
+    expect(secretKeyPacket2.privateParams).to.deep.equal(secretKeyPacket.privateParams);
+    expect(secretKeyPacket2.publicParams).to.deep.equal(secretKeyPacket.publicParams);
   });
 
-  it('Writing and encryption of a secret key packet. (AEAD)', async function() {
-    let aeadProtectVal = openpgp.config.aeadProtect;
-    openpgp.config.aeadProtect = true;
+  it('Writing and encryption of a secret key packet (CFB)', async function() {
+    const aeadProtectVal = openpgp.config.aeadProtect;
+    openpgp.config.aeadProtect = false;
 
-    const key = new openpgp.PacketList();
-    key.push(new openpgp.SecretKeyPacket());
-
-    const rsa = openpgp.crypto.publicKey.rsa;
+    const rsa = openpgp.enums.publicKey.rsaEncryptSign;
     const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
 
     try {
-      const mpiGen = await rsa.generate(keySize, 65537);
-      let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
-      mpi = mpi.map(function(k) {
-        return new openpgp.MPI(k);
-      });
+      const { privateParams, publicParams } = await openpgp.crypto.generateParams(rsa, keySize, 65537);
+      const secretKeyPacket = new openpgp.SecretKeyPacket();
+      secretKeyPacket.privateParams = privateParams;
+      secretKeyPacket.publicParams = publicParams;
+      secretKeyPacket.algorithm = "rsaSign";
+      secretKeyPacket.isEncrypted = false;
+      await secretKeyPacket.encrypt('hello');
 
-      key[0].params = mpi;
-      key[0].algorithm = "rsaSign";
-      key[0].isEncrypted = false;
-      await key[0].encrypt('hello');
-
-      const raw = key.write();
-
-      const key2 = new openpgp.PacketList();
-      await key2.read(raw, openpgp);
-      await key2[0].decrypt('hello');
-
-      expect(key[0].params.toString()).to.equal(key2[0].params.toString());
+      const raw = new openpgp.PacketList();
+      raw.push(secretKeyPacket);
+      const packetList = new openpgp.PacketList();
+      await packetList.read(raw.write(), openpgp);
+      const secretKeyPacket2 = packetList[0];
+      await secretKeyPacket2.decrypt('hello');
     } finally {
       openpgp.config.aeadProtect = aeadProtectVal;
     }
   });
 
-  it('Writing and verification of a signature packet.', function() {
+  it('Writing and verification of a signature packet', function() {
     const key = new openpgp.SecretKeyPacket();
 
-    const rsa = openpgp.crypto.publicKey.rsa;
+    const rsa = openpgp.enums.publicKey.rsaEncryptSign;
     const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
 
-    return rsa.generate(keySize, 65537).then(function(mpiGen) {
-      let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
-      mpi = mpi.map(function(k) {
-        return new openpgp.MPI(k);
-      });
+    return openpgp.crypto.generateParams(rsa, keySize, 65537).then(function({ privateParams, publicParams }) {
       const testText = input.createSomeMessage();
 
-      key.params = mpi;
+      key.publicParams = publicParams;
+      key.privateParams = privateParams;
       key.algorithm = "rsaSign";
 
       const signed = new openpgp.PacketList();
diff --git a/test/general/signature.js b/test/general/signature.js
index 455ba029..1334aa74 100644
--- a/test/general/signature.js
+++ b/test/general/signature.js
@@ -877,9 +877,9 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
       expect(msg.signatures).to.have.length(1);
       expect(msg.signatures[0].valid).to.be.true;
       expect(msg.signatures[0].signature.packets.length).to.equal(1);
-      await expect(openpgp.sign({ message: openpgp.message.fromText('test'), privateKeys: [priv_key_gnupg_ext] })).to.eventually.be.rejectedWith('Missing private key parameters');
-      await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith('Missing private key parameters');
-      await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith('Missing private key parameters');
+      await expect(openpgp.sign({ message: openpgp.message.fromText('test'), privateKeys: [priv_key_gnupg_ext] })).to.eventually.be.rejectedWith(/Missing key parameters/);
+      await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith(/Missing key parameters/);
+      await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith(/Missing key parameters/);
       await priv_key_gnupg_ext.encrypt("abcd");
       expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
       const primaryKey_packet2 = priv_key_gnupg_ext.primaryKey.write();
diff --git a/test/general/testInputs.js b/test/general/testInputs.js
index 8a12cee5..6bcb9e1d 100644
--- a/test/general/testInputs.js
+++ b/test/general/testInputs.js
@@ -1,18 +1,17 @@
-
 /**
  * Generates a 64 character long javascript string out of the whole utf-8 range.
  */
 function createSomeMessage(){
-    let arr = [];
-    for (let i = 0; i < 30; i++) {
-        arr.push(Math.floor(Math.random() * 10174) + 1);
-    }
-    for (let i = 0; i < 10; i++) {
-        arr.push(0x1F600 + Math.floor(Math.random() * (0x1F64F - 0x1F600)) + 1);
-    }
-    return '  \t' + String.fromCodePoint(...arr).replace(/\r/g, '\n') + '  \t\n한국어/조선말';
+  const arr = [];
+  for (let i = 0; i < 30; i++) {
+    arr.push(Math.floor(Math.random() * 10174) + 1);
+  }
+  for (let i = 0; i < 10; i++) {
+    arr.push(0x1F600 + Math.floor(Math.random() * (0x1F64F - 0x1F600)) + 1);
+  }
+  return '  \t' + String.fromCodePoint(...arr).replace(/\r/g, '\n') + '  \t\n한국어/조선말';
 }
 
 module.exports = {
-    createSomeMessage: createSomeMessage
+  createSomeMessage: createSomeMessage
 };
diff --git a/test/general/x25519.js b/test/general/x25519.js
index 6ca608fc..bc6f5573 100644
--- a/test/general/x25519.js
+++ b/test/general/x25519.js
@@ -219,22 +219,24 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
       const { publicKey } = openpgp.crypto.publicKey.nacl.sign.keyPair.fromSeed(openpgp.util.hexToUint8Array(vector.SECRET_KEY));
       expect(publicKey).to.deep.equal(openpgp.util.hexToUint8Array(vector.PUBLIC_KEY));
       const data = util.strToUint8Array(vector.MESSAGE);
-      const keyIntegers = [
-        new openpgp.OID(curve.oid),
-        new openpgp.MPI(util.hexToStr('40'+vector.PUBLIC_KEY)),
-        new openpgp.MPI(util.hexToStr(vector.SECRET_KEY))
-      ];
+      const privateParams = {
+        seed: util.hexToUint8Array(vector.SECRET_KEY)
+      };
+      const publicParams = {
+        oid: new openpgp.OID(curve.oid),
+        Q: util.hexToUint8Array('40' + vector.PUBLIC_KEY)
+      };
       const msg_MPIs = [
         new openpgp.MPI(util.uint8ArrayToStr(util.hexToUint8Array(vector.SIGNATURE.R).reverse())),
         new openpgp.MPI(util.uint8ArrayToStr(util.hexToUint8Array(vector.SIGNATURE.S).reverse()))
       ];
       return Promise.all([
-        signature.sign(22, undefined, keyIntegers, undefined, data).then(signed => {
-          const len = ((signed[0] << 8| signed[1]) + 7) / 8;
+        signature.sign(22, undefined, publicParams, privateParams, undefined, data).then(signed => {
+          const len = (((signed[0] << 8) | signed[1]) + 7) / 8;
           expect(util.hexToUint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len));
           expect(util.hexToUint8Array(vector.SIGNATURE.S)).to.deep.eq(signed.slice(4 + len));
         }),
-        signature.verify(22, undefined, msg_MPIs, keyIntegers, undefined, data).then(result => {
+        signature.verify(22, undefined, msg_MPIs, publicParams, undefined, data).then(result => {
           expect(result).to.be.true;
         })
       ]);
@@ -242,63 +244,44 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
 
     it('Signature of empty string', function () {
       return testVector({
-        SECRET_KEY:
-        ['9d61b19deffd5a60ba844af492ec2cc4',
-         '4449c5697b326919703bac031cae7f60'].join(''),
-        PUBLIC_KEY:
-        ['d75a980182b10ab7d54bfed3c964073a',
-         '0ee172f3daa62325af021a68f707511a'].join(''),
+        SECRET_KEY: '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60',
+        PUBLIC_KEY: 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a',
         MESSAGE: '',
-        SIGNATURE:
-        { R: ['e5564300c360ac729086e2cc806e828a',
-              '84877f1eb8e5d974d873e06522490155'].join(''),
-          S: ['5fb8821590a33bacc61e39701cf9b46b',
-              'd25bf5f0595bbe24655141438e7a100b'].join('') }
+        SIGNATURE: {
+          R: 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155',
+          S: '5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'
+        }
       });
     });
 
     it('Signature of single byte', function () {
       return testVector({
-        SECRET_KEY:
-        ['4ccd089b28ff96da9db6c346ec114e0f',
-         '5b8a319f35aba624da8cf6ed4fb8a6fb'].join(''),
-        PUBLIC_KEY:
-        ['3d4017c3e843895a92b70aa74d1b7ebc',
-         '9c982ccf2ec4968cc0cd55f12af4660c'].join(''),
+        SECRET_KEY: '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb',
+        PUBLIC_KEY: '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c',
         MESSAGE: util.hexToStr('72'),
-        SIGNATURE:
-        { R: ['92a009a9f0d4cab8720e820b5f642540',
-              'a2b27b5416503f8fb3762223ebdb69da'].join(''),
-          S: ['085ac1e43e15996e458f3613d0f11d8c',
-              '387b2eaeb4302aeeb00d291612bb0c00'].join('') }
+        SIGNATURE: {
+          R: '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da',
+          S: '085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'
+        }
       });
     });
 
     it('Signature of two bytes', function () {
       return testVector({
-        SECRET_KEY:
-        ['c5aa8df43f9f837bedb7442f31dcb7b1',
-         '66d38535076f094b85ce3a2e0b4458f7'].join(''),
-        PUBLIC_KEY:
-        ['fc51cd8e6218a1a38da47ed00230f058',
-         '0816ed13ba3303ac5deb911548908025'].join(''),
+        SECRET_KEY: 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7',
+        PUBLIC_KEY: 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025',
         MESSAGE: util.hexToStr('af82'),
-        SIGNATURE:
-        { R: ['6291d657deec24024827e69c3abe01a3',
-              '0ce548a284743a445e3680d7db5ac3ac'].join(''),
-          S: ['18ff9b538d16f290ae67f760984dc659',
-              '4a7c15e9716ed28dc027beceea1ec40a'].join('') }
+        SIGNATURE: {
+          R: '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac',
+          S: '18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a'
+        }
       });
     });
 
     it('Signature of 1023 bytes', function () {
       return testVector({
-        SECRET_KEY:
-        ['f5e5767cf153319517630f226876b86c',
-         '8160cc583bc013744c6bf255f5cc0ee5'].join(''),
-        PUBLIC_KEY:
-        ['278117fc144c72340f67d0f2316e8386',
-         'ceffbf2b2428c9c51fef7c597f1d426e'].join(''),
+        SECRET_KEY: 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5',
+        PUBLIC_KEY: '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e',
         MESSAGE: util.hexToStr([
           '08b8b2b733424243760fe426a4b54908',
           '632110a66c2f6591eabd3345e3e4eb98',
@@ -365,39 +348,32 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
           '0618983f8741c5ef68d3a101e8a3b8ca',
           'c60c905c15fc910840b94c00a0b9d0'
         ].join('')),
-        SIGNATURE:
-        { R: ['0aab4c900501b3e24d7cdf4663326a3a',
-              '87df5e4843b2cbdb67cbf6e460fec350'].join(''),
-          S: ['aa5371b1508f9f4528ecea23c436d94b',
-              '5e8fcd4f681e30a6ac00a9704a188a03'].join('') }
+        SIGNATURE: {
+          R: '0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350',
+          S: 'aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'
+        }
       });
     });
 
     it('Signature of SHA(abc)', function () {
       return testVector({
-        SECRET_KEY:
-        ['833fe62409237b9d62ec77587520911e',
-         '9a759cec1d19755b7da901b96dca3d42'].join(''),
-        PUBLIC_KEY:
-        ['ec172b93ad5e563bf4932c70e1245034',
-         'c35467ef2efd4d64ebf819683467e2bf'].join(''),
+        SECRET_KEY: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
+        PUBLIC_KEY: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
         MESSAGE: util.hexToStr([
           'ddaf35a193617abacc417349ae204131',
           '12e6fa4e89a97ea20a9eeee64b55d39a',
           '2192992a274fc1a836ba3c23a3feebbd',
           '454d4423643ce80e2a9ac94fa54ca49f'
         ].join('')),
-        SIGNATURE:
-        { R: ['dc2a4459e7369633a52b1bf277839a00',
-              '201009a3efbf3ecb69bea2186c26b589'].join(''),
-          S: ['09351fc9ac90b3ecfdfbc7c66431e030',
-              '3dca179c138ac17ad9bef1177331a704'].join('') }
+        SIGNATURE: {
+          R: 'dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589',
+          S: '09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704'
+        }
       });
     });
   });
 
-/* TODO how does GPG2 accept this?
-  it('Should handle little-endian parameters in EdDSA', function () {
+  it('Should handle little-endian parameters in EdDSA', async function () {
     const pubKey = [
       '-----BEGIN PGP PUBLIC KEY BLOCK-----',
       'Version: OpenPGP.js v3.0.0',
@@ -412,19 +388,18 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
       'FQIbDAAAhNQBAKmy4gPorjbwTwy5usylHttP28XnTdaGkZ1E7Rc3G9luAQCs',
       'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==',
       '=xeG/',
-      '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
+      '-----END PGP PUBLIC KEY BLOCK-----'
+    ].join('\n');
     const hi = await openpgp.key.readArmored(pubKey);
-    const results = hi.getPrimaryUser();
+    const results = await hi.getPrimaryUser();
+    // console.log(results);
     expect(results).to.exist;
     expect(results.user).to.exist;
     const user = results.user;
-    expect(user.selfCertifications[0].verify(
-      hi.primaryKey, {userId: user.userId, key: hi.primaryKey}
-    )).to.eventually.be.true;
     await user.verifyCertificate(
       hi.primaryKey, user.selfCertifications[0], [hi]
     );
-  }); */
+  });
 
   describe('X25519 Omnibus Tests', omnibus);
 });