diff --git a/README.md b/README.md
index a62a6641..92bbfb61 100644
--- a/README.md
+++ b/README.md
@@ -240,6 +240,30 @@ var options = {
 openpgp.generateKey(options).then(function(key) {
     var privkey = key.privateKeyArmored; // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
     var pubkey = key.publicKeyArmored;   // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
+    var revocationSignature = key.revocationSignature; // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
+});
+```
+
+#### Revoke a key
+
+Using a revocation signature:
+```js
+var options = {
+    key: openpgp.key.readArmored(pubkey).keys[0],
+    revocationSignature: revocationSignature
+};
+```
+
+Using the private key:
+```js
+var options = {
+    key: openpgp.key.readArmored(privkey).keys[0]
+};
+```
+
+```js
+openpgp.revokeKey(options).then(function(key) {
+    var pubkey = key.publicKeyArmored;   // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
 });
 ```
 
diff --git a/src/index.js b/src/index.js
index 7b2b7f14..e0cd2f3c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -19,7 +19,7 @@ export default openpgp;
  */
 export {
   encrypt, decrypt, sign, verify,
-  generateKey, reformatKey, decryptKey,
+  generateKey, reformatKey, revokeKey, decryptKey,
   encryptSessionKey, decryptSessionKeys,
   initWorker, getWorker, destroyWorker
 } from './openpgp';
diff --git a/src/key.js b/src/key.js
index 118ed8ce..8713cfac 100644
--- a/src/key.js
+++ b/src/key.js
@@ -665,6 +665,56 @@ Key.prototype.revoke = async function(privateKey, {
   return key;
 };
 
+/**
+ * Get revocation certificate from a revoked key.
+ *   (To get a revocation certificate for an unrevoked key, call revoke() first.)
+ * @return {String} armored revocation certificate
+ */
+Key.prototype.getRevocationCertificate = function() {
+  if (this.revocationSignature) {
+    const commentstring = config.commentstring;
+    config.commentstring = 'This is a revocation certificate';
+    try {
+      const packetlist = new packet.List();
+      packetlist.push(this.revocationSignature);
+      return armor.encode(enums.armor.public_key, packetlist.write());
+    } finally {
+      // Restore comment string. armor.encode() shouldn't throw, but just to be sure it's wrapped in a try/finally
+      config.commentstring = commentstring;
+    }
+  }
+};
+
+/**
+ * Applies a revocation certificate to a key
+ * @param  {String} revocationCertificate armored revocation certificate
+ * @return {module:key~Key} new revoked key
+ */
+Key.prototype.applyRevocationCertificate = async function(revocationCertificate) {
+  const input = armor.decode(revocationCertificate);
+  if (input.type !== enums.armor.public_key) {
+    throw new Error('Armored text not of type public key');
+  }
+  const packetlist = new packet.List();
+  packetlist.read(input.data);
+  const revocationSignature = packetlist.findPacket(enums.packet.signature);
+  if (!revocationSignature || revocationSignature.signatureType !== enums.signature.key_revocation) {
+    throw new Error('Could not find revocation signature packet');
+  }
+  if (!revocationSignature.issuerKeyId.equals(this.primaryKey.getKeyId())) {
+    throw new Error('Revocation signature does not match key');
+  }
+  if (revocationSignature.isExpired()) {
+    throw new Error('Revocation signature is expired');
+  }
+  if (!await revocationSignature.verify(this.primaryKey, { key: this.primaryKey })) {
+    throw new Error('Could not verify revocation signature');
+  }
+  const key = new Key(this.toPacketlist());
+  key.revocationSignature = revocationSignature;
+  return key;
+};
+
 /**
  * Signs primary user of key
  * @param  {Array<module:key.Key>} privateKey decrypted private keys for signing
@@ -1185,6 +1235,7 @@ export function readArmored(armoredText) {
  * @param  {Date} date         Override the creation date of the key and the key signatures
  * @param  {Array<Object>} subkeys   (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
  *                                              sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
+ * @param {Boolean} [options.revoked=false] Whether the key should include a revocation signature
  * @returns {Promise<module:key.Key>}
  * @async
  * @static
@@ -1266,6 +1317,7 @@ export async function generate(options) {
  * @param  {Date} date         Override the creation date of the key and the key signatures
  * @param  {Array<Object>} subkeys   (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
  *
+ * @param {Boolean} [options.revoked=false] Whether the key should include a revocation signature
  * @returns {Promise<module:key.Key>}
  * @async
  * @static
@@ -1416,6 +1468,19 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
     });
   });
 
+  if (options.revoked) {
+    const dataToSign = {};
+    dataToSign.key = secretKeyPacket;
+    const revocationSignaturePacket = new packet.Signature();
+    revocationSignaturePacket.signatureType = enums.signature.key_revocation;
+    revocationSignaturePacket.reasonForRevocationFlag = enums.reasonForRevocation.no_reason;
+    revocationSignaturePacket.reasonForRevocationString = '';
+    revocationSignaturePacket.publicKeyAlgorithm = options.keyType;
+    revocationSignaturePacket.hashAlgorithm = getPreferredHashAlgo(secretKeyPacket);
+    await revocationSignaturePacket.sign(secretKeyPacket, dataToSign);
+    packetlist.push(revocationSignaturePacket);
+  }
+
   // set passphrase protection
   if (options.passphrase) {
     secretKeyPacket.clearPrivateParams();
diff --git a/src/openpgp.js b/src/openpgp.js
index 7bc4706e..37c279a1 100644
--- a/src/openpgp.js
+++ b/src/openpgp.js
@@ -108,15 +108,16 @@ export function destroyWorker() {
  * @param  {Date} date               (optional) override the creation date of the key and the key signatures
  * @param  {Array<Object>} subkeys   (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
  *                                              sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
+ * @param  {Boolean} revocationCertificate (optional) Whether the returned object should include a revocation certificate to revoke the public key
  * @returns {Promise<Object>}         The generated key object in the form:
- *                                     { key:Key, privateKeyArmored:String, publicKeyArmored:String }
+ *                                     { key:Key, privateKeyArmored:String, publicKeyArmored:String, revocationCertificate:String }
  * @async
  * @static
  */
 
-export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpirationTime=0, curve="", date=new Date(), subkeys=[{}] }) {
+export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpirationTime=0, curve="", date=new Date(), subkeys=[{}], revocationCertificate=true }) {
   userIds = toArray(userIds);
-  const options = { userIds, passphrase, numBits, keyExpirationTime, curve, date, subkeys };
+  const options = { userIds, passphrase, numBits, keyExpirationTime, curve, date, subkeys, revocationCertificate };
   if (util.getWebCryptoAll() && numBits < 2048) {
     throw new Error('numBits should be 2048 or 4096, found: ' + numBits);
   }
@@ -125,13 +126,21 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
     return asyncProxy.delegate('generateKey', options);
   }
 
-  return generate(options).then(key => ({
+  options.revoked = options.revocationCertificate;
 
-    key: key,
-    privateKeyArmored: key.armor(),
-    publicKeyArmored: key.toPublic().armor()
+  return generate(options).then(key => {
+    const revocationCertificate = key.getRevocationCertificate();
+    key.revocationSignature = null;
 
-  })).catch(onError.bind(null, 'Error generating keypair'));
+    return {
+
+      key: key,
+      privateKeyArmored: key.armor(),
+      publicKeyArmored: key.toPublic().armor(),
+      revocationCertificate: revocationCertificate
+
+    };
+  }).catch(onError.bind(null, 'Error generating keypair'));
 }
 
 /**
@@ -140,25 +149,81 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
  * @param  {Array<Object>} userIds   array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }]
  * @param  {String} passphrase       (optional) The passphrase used to encrypt the resulting private key
  * @param  {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
+ * @param  {Boolean} revocationCertificate (optional) Whether the returned object should include a revocation certificate to revoke the public key
  * @returns {Promise<Object>}         The generated key object in the form:
- *                                     { key:Key, privateKeyArmored:String, publicKeyArmored:String }
+ *                                     { key:Key, privateKeyArmored:String, publicKeyArmored:String, revocationCertificate:String }
  * @async
  * @static
  */
-export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date}) {
+export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date, revocationCertificate=true}) {
   userIds = toArray(userIds);
-  const options = { privateKey, userIds, passphrase, keyExpirationTime, date};
+  const options = { privateKey, userIds, passphrase, keyExpirationTime, date, revocationCertificate=true};
   if (asyncProxy) {
     return asyncProxy.delegate('reformatKey', options);
   }
 
-  return reformat(options).then(key => ({
+  options.revoked = options.revocationCertificate;
 
-    key: key,
-    privateKeyArmored: key.armor(),
-    publicKeyArmored: key.toPublic().armor()
+  return reformat(options).then(key => {
+    const revocationCertificate = key.getRevocationCertificate();
+    key.revocationSignature = null;
 
-  })).catch(onError.bind(null, 'Error reformatting keypair'));
+    return {
+
+      key: key,
+      privateKeyArmored: key.armor(),
+      publicKeyArmored: key.toPublic().armor(),
+      revocationCertificate: revocationCertificate
+
+    };
+  }).catch(onError.bind(null, 'Error reformatting keypair'));
+}
+
+/**
+ * Revokes a key. Requires either a private key or a revocation certificate.
+ *   If a revocation certificate is passed, the reasonForRevocation parameters will be ignored.
+ * @param  {Key} key                 (optional) public or private key to revoke
+ * @param  {String} revocationCertificate (optional) revocation certificate to revoke the key with
+ * @param  {Object} reasonForRevocation (optional) object indicating the reason for revocation
+ * @param  {module:enums.reasonForRevocation} reasonForRevocation.flag (optional) flag indicating the reason for revocation
+ * @param  {String} reasonForRevocation.string (optional) string explaining the reason for revocation
+ * @return {Promise<Object>}         The revoked key object in the form:
+ *                                     { privateKey:Key, privateKeyArmored:String, publicKey:Key, publicKeyArmored:String }
+ *                                     (if private key is passed) or { publicKey:Key, publicKeyArmored:String } (otherwise)
+ * @static
+ */
+export function revokeKey({
+  key, revocationCertificate, reasonForRevocation
+} = {}) {
+  const options = {
+    key, revocationCertificate, reasonForRevocation
+  };
+
+  if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported
+    return asyncProxy.delegate('revokeKey', options);
+  }
+
+  return Promise.resolve().then(() => {
+    if (revocationCertificate) {
+      return key.applyRevocationCertificate(revocationCertificate);
+    } else {
+      return key.revoke(key, reasonForRevocation);
+    }
+  }).then(key => {
+    if(key.isPrivate()) {
+      const publicKey = key.toPublic();
+      return {
+        privateKey: key,
+        privateKeyArmored: key.armor(),
+        publicKey: publicKey,
+        publicKeyArmored: publicKey.armor()
+      };
+    }
+    return {
+      publicKey: key,
+      publicKeyArmored: key.armor()
+    };
+  }).catch(onError.bind(null, 'Error revoking key'));
 }
 
 /**
diff --git a/test/general/key.js b/test/general/key.js
index 4ee5970e..4d6f319f 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -114,6 +114,97 @@ function tests() {
       'hz3tYjKhoFTKEIq3y3Pp',
       '=h/aX',
       '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
+      
+  const pub_key_arm4 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG 2.1.15 (GNU/Linux)
+
+mI0EWpoNLAEEAMoO8dfnLvvCze1hjWcr8t1fMdndFQc1fAM7dm6sbqrdlaAz+Dab
+zF3F9UhIOCcABRm+QHyZlgEsoQpHF/7sWflUK1FpoxdORINtIDilukUkMZ0NnIaD
++8pRutdSczPNFvSImSzZNCyLzvDCGMO3+Xeaa6pViSPEeBwhXWJUuHYtABEBAAG0
+IkpvZSBVc2VyIDxqb2UudXNlckBwcm90b25tYWlsLmNvbT6ItwQTAQgAIQUCWpoN
+LAIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBYtrN4WUnCtoUjBACi6qVb
+noQJ1aOaOyoBZscDIO5XZHZK4L9V9uJJhD9v1qRF5s0PRiG969EwFlvjqIlLiRej
+KSxvE/1rcym4GwBUndku1fMM+weTWHNtRn9+BPzN/4eKZbUbY3fHtlk+Lde3N+CZ
+vrGKS/ICtbtuAfZL0LdzzqnNuBUXlO6EpG5C3riNBFqaDSwBBADDURzGkpTn/FTT
+xHyheai+zTOUmy7N1ViCRPkErIeD606tZf/sKqAnEChfECeZJReYydN1B3O8QOyI
+Ly/rH0DS2bt/6juhknPVGHPUAyNxHmiHYXTUgGPEX1QfusjzBcfIk6vHjYBiRm/I
+u9iwrzCwypA4dWDZSTZuFrVsf4n+twARAQABiJ8EGAEIAAkFAlqaDSwCGwwACgkQ
+WLazeFlJwrZQEAQAuUrp9Qp76CnKqUsUjcVxq7DJBi/lewyGGYSVAFt6/0Xyg/8Y
+TEa/c4Dri/HMOtrfbgjp/doIVaZLOXZYfqRcpy3z0M6BierOPB3D+fdaTfd7gIrQ
+nGHIp2NmbJZnYgl8Ps23qF+LKTa1eE+AmMQYzUHSGuka2lp6OglwWzg/dEw=
+=/vbH
+-----END PGP PUBLIC KEY BLOCK-----`;
+
+  const priv_key_arm4 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG 2.1.15 (GNU/Linux)
+
+lQIGBFqaDSwBBADKDvHX5y77ws3tYY1nK/LdXzHZ3RUHNXwDO3ZurG6q3ZWgM/g2
+m8xdxfVISDgnAAUZvkB8mZYBLKEKRxf+7Fn5VCtRaaMXTkSDbSA4pbpFJDGdDZyG
+g/vKUbrXUnMzzRb0iJks2TQsi87wwhjDt/l3mmuqVYkjxHgcIV1iVLh2LQARAQAB
+/gcDAoZ8RULY7umS4fVGPmTuETCnOOTGancXT5r7chKyfFXlyVU4ULvTdLwdFtqx
+Vl9tNyED31nIiRP1CTmZLeaVScNGfVLjo8nvpMZUVopw5UdaFADeVTpwVdtp7ru+
+IgH4ynrRMgMGh7/dgBzIP8WN4w8uBPK5G4bS34NNiREkVoZ3oh4dA/6aeYfW7lVV
+cYRl2F7++AGfqS+FpLsE8KjFU2z8POJjWMN1nYKwjNa+beEO0BFYdUFvMzU7eUHA
+/G0xWAhYvNyuJHE4imgYmCy1OZeawc9h8YGeaQJCh2NTVzaD9HRu0xmz93bNF19q
+bfUZJC7mC6WzKsRXHX0JmzH+9DShUqGnkRl5fMo2UhQMpSxsMT4dU/Ji4q+t96oy
+K6g3DMJr5OtZML3XKxGmdy0CgepkG1aikrC9qLfBgxjqi+uTewcbrS9lAOfrZg4N
+jwt1FLEK8gu7aOeczdW/pFOHOCrX1DnpF81JKJ1a7hz5JRP1m+ffqwm0IkpvZSBV
+c2VyIDxqb2UudXNlckBwcm90b25tYWlsLmNvbT6ItwQTAQgAIQUCWpoNLAIbAwUL
+CQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBYtrN4WUnCtoUjBACi6qVbnoQJ1aOa
+OyoBZscDIO5XZHZK4L9V9uJJhD9v1qRF5s0PRiG969EwFlvjqIlLiRejKSxvE/1r
+cym4GwBUndku1fMM+weTWHNtRn9+BPzN/4eKZbUbY3fHtlk+Lde3N+CZvrGKS/IC
+tbtuAfZL0LdzzqnNuBUXlO6EpG5C3p0CBgRamg0sAQQAw1EcxpKU5/xU08R8oXmo
+vs0zlJsuzdVYgkT5BKyHg+tOrWX/7CqgJxAoXxAnmSUXmMnTdQdzvEDsiC8v6x9A
+0tm7f+o7oZJz1Rhz1AMjcR5oh2F01IBjxF9UH7rI8wXHyJOrx42AYkZvyLvYsK8w
+sMqQOHVg2Uk2bha1bH+J/rcAEQEAAf4HAwLwNvRIoBFS3OHTIYirkr4sHzSkWFJx
+xDPozovXgCq7BoCXDMaSIQLwZqEfb+SabYtk7nLSnG2Y2mgwb9swZuBZEWuQjZk7
+lX1MvuZ0Ih2QdQSMEJk8sEsMoBGHHdHh/MZO4a27+5B9OceDfnEZZcGSOweUuu1n
+IlgWcgrM40q4S3Mt39FXFgdJWnpd93hAokKDHklUGMdMLw/02dGVRkJmvUp9qdhe
+c2njq9HSeYwqbY2rYgcNsF2ZcCLt9UXA2dOG4X2c2mPfjKuTRZUPxNKh6JfL3mlu
+rBdd/z8gQHoKObyaarVwN3HAbtP0+6Z8a9/wDYj1K9ZCoHuEtKq1qq5J2Ec8+Yzl
+K0Zlcs760LiYUr69CninMrnbDNnAhrYAcyJS42viUADPv9g+CBbyanB4KyE4UNrZ
+BCB296lOEW4v1IZVNrNvqrbka3/p0qqBJiFTh7eT3zXpRNArFZDmLCUEEm53qT1a
+PO/MyYUGTTMRAzTmNTiPiJ8EGAEIAAkFAlqaDSwCGwwACgkQWLazeFlJwrZQEAQA
+uUrp9Qp76CnKqUsUjcVxq7DJBi/lewyGGYSVAFt6/0Xyg/8YTEa/c4Dri/HMOtrf
+bgjp/doIVaZLOXZYfqRcpy3z0M6BierOPB3D+fdaTfd7gIrQnGHIp2NmbJZnYgl8
+Ps23qF+LKTa1eE+AmMQYzUHSGuka2lp6OglwWzg/dEw=
+=mr3M
+-----END PGP PRIVATE KEY BLOCK-----`;
+
+  const revocation_certificate_arm4 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG 2.1.15 (GNU/Linux)
+Comment: This is a revocation certificate
+
+iJ8EIAEIAAkFAlqaDT0CHQAACgkQWLazeFlJwrbOaAP/V38FhBrUy4XYgt8ZX22G
+ov6IFDNoyRKafSuz7Rg+8K8cf+0MAsSi52ueKfsbPxQ+I1vPeaEuEYbwTjtbvM+M
+vZcX+VNYdsc1iZeNaT4ayA+2LrCN/xgFj0nrExHqcZAjgBZ9pvKghAqdK4Zb2Ghb
+7chPiLLNWJCMtL4bo7a1X84=
+=HcWg
+-----END PGP PUBLIC KEY BLOCK-----`;
+
+  const revoked_key_arm4 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG 2.1.15 (GNU/Linux)
+
+mI0EWpoNLAEEAMoO8dfnLvvCze1hjWcr8t1fMdndFQc1fAM7dm6sbqrdlaAz+Dab
+zF3F9UhIOCcABRm+QHyZlgEsoQpHF/7sWflUK1FpoxdORINtIDilukUkMZ0NnIaD
++8pRutdSczPNFvSImSzZNCyLzvDCGMO3+Xeaa6pViSPEeBwhXWJUuHYtABEBAAGI
+nwQgAQgACQUCWpoNPQIdAAAKCRBYtrN4WUnCts5oA/9XfwWEGtTLhdiC3xlfbYai
+/ogUM2jJEpp9K7PtGD7wrxx/7QwCxKLna54p+xs/FD4jW895oS4RhvBOO1u8z4y9
+lxf5U1h2xzWJl41pPhrID7YusI3/GAWPSesTEepxkCOAFn2m8qCECp0rhlvYaFvt
+yE+Iss1YkIy0vhujtrVfzrQiSm9lIFVzZXIgPGpvZS51c2VyQHByb3Rvbm1haWwu
+Y29tPoi3BBMBCAAhBQJamg0sAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJ
+EFi2s3hZScK2hSMEAKLqpVuehAnVo5o7KgFmxwMg7ldkdkrgv1X24kmEP2/WpEXm
+zQ9GIb3r0TAWW+OoiUuJF6MpLG8T/WtzKbgbAFSd2S7V8wz7B5NYc21Gf34E/M3/
+h4pltRtjd8e2WT4t17c34Jm+sYpL8gK1u24B9kvQt3POqc24FReU7oSkbkLeuI0E
+WpoNLAEEAMNRHMaSlOf8VNPEfKF5qL7NM5SbLs3VWIJE+QSsh4PrTq1l/+wqoCcQ
+KF8QJ5klF5jJ03UHc7xA7IgvL+sfQNLZu3/qO6GSc9UYc9QDI3EeaIdhdNSAY8Rf
+VB+6yPMFx8iTq8eNgGJGb8i72LCvMLDKkDh1YNlJNm4WtWx/if63ABEBAAGInwQY
+AQgACQUCWpoNLAIbDAAKCRBYtrN4WUnCtlAQBAC5Sun1CnvoKcqpSxSNxXGrsMkG
+L+V7DIYZhJUAW3r/RfKD/xhMRr9zgOuL8cw62t9uCOn92ghVpks5dlh+pFynLfPQ
+zoGJ6s48HcP591pN93uAitCcYcinY2ZslmdiCXw+zbeoX4spNrV4T4CYxBjNQdIa
+6RraWno6CXBbOD90TA==
+=8d2d
+-----END PGP PUBLIC KEY BLOCK-----`;
 
   const twoKeys =
        ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
@@ -1467,6 +1558,34 @@ const mergeKey2 = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
     });
   });
 
+  it('applyRevocationCertificate() should produce the same revoked key as GnuPG', function() {
+    const pubKey = openpgp.key.readArmored(pub_key_arm4).keys[0];
+
+    return pubKey.applyRevocationCertificate(revocation_certificate_arm4).then(revKey => {
+      expect(revKey.armor()).to.equal(openpgp.key.readArmored(revoked_key_arm4).keys[0].armor());
+    });
+  });
+
+  it('getRevocationCertificate() should produce the same revocation certificate as GnuPG', function() {
+    const revKey = openpgp.key.readArmored(revoked_key_arm4).keys[0];
+    const revocationCertificate = revKey.getRevocationCertificate();
+
+    const input = openpgp.armor.decode(revocation_certificate_arm4);
+    const packetlist = new openpgp.packet.List();
+    packetlist.read(input.data);
+    const armored = openpgp.armor.encode(openpgp.enums.armor.public_key, packetlist.write());
+
+    expect(revocationCertificate.replace(/^Comment: .*$/m, '')).to.equal(armored.replace(/^Comment: .*$/m, ''));
+  });
+
+  it('getRevocationCertificate() should have an appropriate comment', function() {
+    const revKey = openpgp.key.readArmored(revoked_key_arm4).keys[0];
+    const revocationCertificate = revKey.getRevocationCertificate();
+
+    expect(revocationCertificate).to.match(/Comment: This is a revocation certificate/);
+    expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
+  });
+
   it("getPreferredAlgo('symmetric') - one key - AES256", async function() {
     const key1 = openpgp.key.readArmored(twoKeys).keys[0];
     const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1]);
@@ -2012,6 +2131,37 @@ const mergeKey2 = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
     });
   });
 
+  it('Revoke generated key with revocation certificate', function() {
+    const opt = {numBits: 512, userIds: 'test1 <a@b.com>', passphrase: '1234'};
+    if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
+    return openpgp.generateKey(opt).then(function(original) {
+      return openpgp.revokeKey({key: original.key.toPublic(), revocationCertificate: original.revocationCertificate}).then(function(revKey) {
+        revKey = revKey.publicKey;
+        expect(revKey.revocationSignature.reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
+        expect(revKey.revocationSignature.reasonForRevocationString).to.equal('');
+        return revKey.verifyPrimaryKey().then(function(status) {
+          expect(status).to.equal(openpgp.enums.keyStatus.revoked);
+        });
+      });
+    });
+  });
+
+  it('Revoke generated key with private key', function() {
+    const opt = {numBits: 512, userIds: 'test1 <a@b.com>', passphrase: '1234'};
+    if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
+    return openpgp.generateKey(opt).then(function(original) {
+      original.key.decrypt('1234');
+      return openpgp.revokeKey({key: original.key, reasonForRevocation: {string: 'Testing key revocation'}}).then(function(revKey) {
+        revKey = revKey.publicKey;
+        expect(revKey.revocationSignature.reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
+        expect(revKey.revocationSignature.reasonForRevocationString).to.equal('Testing key revocation');
+        return revKey.verifyPrimaryKey().then(function(status) {
+          expect(status).to.equal(openpgp.enums.keyStatus.revoked);
+        });
+      });
+    });
+  });
+
   it('Merge key with another key with non-ID user attributes', function(done) {
     const key = openpgp.key.readArmored(mergeKey1).keys[0];
     const updateKey = openpgp.key.readArmored(mergeKey2).keys[0];
diff --git a/test/general/openpgp.js b/test/general/openpgp.js
index a94c676c..4b9f9aac 100644
--- a/test/general/openpgp.js
+++ b/test/general/openpgp.js
@@ -484,6 +484,10 @@ describe('OpenPGP.js public api tests', function() {
               return 'pub_key';
             }
           };
+        },
+        getRevocationCertificate: function() {},
+        removeRevocationCertificate: function() {
+          return this;
         }
       };
       keyGenStub = stub(openpgp.key, 'generate');
@@ -514,6 +518,8 @@ describe('OpenPGP.js public api tests', function() {
           curve: "",
           date: now,
           subkeys: [],
+          revocationCertificate: true,
+          revoked: true,
         }).calledOnce).to.be.true;
         expect(newKey.key).to.exist;
         expect(newKey.privateKeyArmored).to.exist;
@@ -1855,6 +1861,36 @@ describe('OpenPGP.js public api tests', function() {
                 expect(signatures[0].signature.packets.length).to.equal(1);
             });
         });
+
+        it.skip('should fail to encrypt with revoked key', function() {
+          return openpgp.revokeKey({
+            key: privateKey.keys[0]
+          }).then(function(revKey) {
+            return openpgp.encrypt({
+              data: plaintext,
+              publicKeys: revKey.publicKey
+            }).then(function(encrypted) {
+              throw new Error('Should not encrypt with revoked key');
+            }).catch(function(error) {
+              expect(error.message).to.match(/Could not find valid key packet for encryption/);
+            });
+          });
+        });
+
+        it.skip('should fail to encrypt with revoked subkey', function() {
+          let clonedKey = privateKey.keys[0].toPublic();
+          return clonedKey.subKeys[0].revoke(clonedKey.primaryKey, privateKey.keys[0]).then(function(revSubKey) {
+            clonedKey.subKeys[0] = revSubKey;
+            return openpgp.encrypt({
+              data: plaintext,
+              publicKeys: clonedKey
+            }).then(function(encrypted) {
+              throw new Error('Should not encrypt with revoked subkey');
+            }).catch(function(error) {
+              expect(error.message).to.match(/Could not find valid key packet for encryption/);
+            });
+          });
+        });
       });
 
       describe('ELG / DSA encrypt, decrypt, sign, verify', function() {