diff --git a/openpgp.d.ts b/openpgp.d.ts
index d08aa14a..cf8f1d12 100644
--- a/openpgp.d.ts
+++ b/openpgp.d.ts
@@ -30,7 +30,7 @@ export abstract class Key {
   public revocationSignatures: SignaturePacket[];
   public write(): Uint8Array;
   public armor(config?: Config): string;
-  public getExpirationTime(capability?: 'encrypt' | 'encrypt_sign' | 'sign', keyID?: KeyID, userID?: UserID, config?: Config): Promise<Date | typeof Infinity | null>; // Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid.
+  public getExpirationTime(userID?: UserID, config?: Config): Promise<Date | typeof Infinity | null>;
   public getKeyIDs(): KeyID[];
   public getPrimaryUser(date?: Date, userID?: UserID, config?: Config): Promise<PrimaryUser>; // throws on error
   public getUserIDs(): string[];
diff --git a/src/key/helper.js b/src/key/helper.js
index 987d0831..6cb48d65 100644
--- a/src/key/helper.js
+++ b/src/key/helper.js
@@ -72,7 +72,7 @@ export function isDataExpired(keyPacket, signature, date = new Date()) {
   const normDate = util.normalizeDate(date);
   if (normDate !== null) {
     const expirationTime = getKeyExpirationTime(keyPacket, signature);
-    return !(keyPacket.created <= normDate && normDate <= expirationTime);
+    return !(keyPacket.created <= normDate && normDate < expirationTime);
   }
   return false;
 }
diff --git a/src/key/key.js b/src/key/key.js
index 3d40fed4..205340a3 100644
--- a/src/key/key.js
+++ b/src/key/key.js
@@ -360,45 +360,49 @@ class Key {
     if (helper.isDataExpired(primaryKey, selfCertification, date)) {
       throw new Error('Primary key is expired');
     }
+    // check for expiration time in direct signatures
+    const directSignature = await helper.getLatestValidSignature(
+      this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config
+    ).catch(() => {}); // invalid signatures are discarded, to avoid breaking the key
+
+    if (directSignature && helper.isDataExpired(primaryKey, directSignature, date)) {
+      throw new Error('Primary key is expired');
+    }
   }
 
   /**
-   * Returns the latest date when the key can be used for encrypting, signing, or both, depending on the `capabilities` paramater.
-   * When `capabilities` is null, defaults to returning the expiry date of the primary key.
-   * Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid.
-   * Returns Infinity if the key doesn't expire.
-   * @param  {encrypt|sign|encrypt_sign} [capabilities] - capabilities to look up
-   * @param  {module:type/keyid~KeyID} [keyID] - key ID of the specific key to check
+   * Returns the expiration date of the primary key, considering self-certifications and direct-key signatures.
+   * Returns `Infinity` if the key doesn't expire, or `null` if the key is revoked or invalid.
    * @param  {Object} [userID] - User ID to consider instead of the primary user
    * @param  {Object} [config] - Full configuration, defaults to openpgp.config
    * @returns {Promise<Date | Infinity | null>}
    * @async
    */
-  async getExpirationTime(capabilities, keyID, userID, config = defaultConfig) {
-    const primaryUser = await this.getPrimaryUser(null, userID, config);
-    const selfCert = primaryUser.selfCertification;
-    const keyExpiry = helper.getKeyExpirationTime(this.keyPacket, selfCert);
-    const sigExpiry = selfCert.getExpirationTime();
-    let expiry = keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
-    if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
-      const encryptKey =
-        await this.getEncryptionKey(keyID, expiry, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {}) ||
-        await this.getEncryptionKey(keyID, null, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {});
-      if (!encryptKey) return null;
-      const encryptExpiry = await encryptKey.getExpirationTime(null, config);
-      if (encryptExpiry < expiry) expiry = encryptExpiry;
+  async getExpirationTime(userID, config = defaultConfig) {
+    let primaryKeyExpiry;
+    try {
+      const { selfCertification } = await this.getPrimaryUser(null, userID, config);
+      const selfSigKeyExpiry = helper.getKeyExpirationTime(this.keyPacket, selfCertification);
+      const selfSigExpiry = selfCertification.getExpirationTime();
+      const directSignature = await helper.getLatestValidSignature(
+        this.directSignatures, this.keyPacket, enums.signature.key, { key: this.keyPacket }, null, config
+      ).catch(() => {});
+      if (directSignature) {
+        const directSigKeyExpiry = helper.getKeyExpirationTime(this.keyPacket, directSignature);
+        // We do not support the edge case where the direct signature expires, since it would invalidate the corresponding key expiration,
+        // causing a discountinous validy period for the key
+        primaryKeyExpiry = Math.min(selfSigKeyExpiry, selfSigExpiry, directSigKeyExpiry);
+      } else {
+        primaryKeyExpiry = selfSigKeyExpiry < selfSigExpiry ? selfSigKeyExpiry : selfSigExpiry;
+      }
+    } catch (e) {
+      primaryKeyExpiry = null;
     }
-    if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
-      const signKey =
-        await this.getSigningKey(keyID, expiry, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {}) ||
-        await this.getSigningKey(keyID, null, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {});
-      if (!signKey) return null;
-      const signExpiry = await signKey.getExpirationTime(null, config);
-      if (signExpiry < expiry) expiry = signExpiry;
-    }
-    return expiry;
+
+    return util.normalizeDate(primaryKeyExpiry);
   }
 
+
   /**
    * Returns primary user and most significant (latest valid) self signature
    * - if multiple primary users exist, returns the one with the latest self signature
diff --git a/src/packet/signature.js b/src/packet/signature.js
index e83aae79..78d74c79 100644
--- a/src/packet/signature.js
+++ b/src/packet/signature.js
@@ -706,7 +706,7 @@ class SignaturePacket {
     if (normDate && this.created > normDate) {
       throw new Error('Signature creation time is in the future');
     }
-    if (normDate && normDate > this.getExpirationTime()) {
+    if (normDate && normDate >= this.getExpirationTime()) {
       throw new Error('Signature is expired');
     }
     if (config.rejectHashAlgorithms.has(hashAlgorithm)) {
@@ -734,7 +734,7 @@ class SignaturePacket {
   isExpired(date = new Date()) {
     const normDate = util.normalizeDate(date);
     if (normDate !== null) {
-      return !(this.created <= normDate && normDate <= this.getExpirationTime());
+      return !(this.created <= normDate && normDate < this.getExpirationTime());
     }
     return false;
   }
diff --git a/test/general/key.js b/test/general/key.js
index dbe507b6..315b21b0 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -2074,6 +2074,57 @@ usLw5q4tc+I5gdq57aiulJ8r4Jj9rdzsZFA7PzNJ9WPGVYJ3
 =GSXO
 -----END PGP PRIVATE KEY BLOCK-----`;
 
+const expiredPublicKeyThroughDirectSignature = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+Be4ubVrj5KjhX2PVNEJd3XZRzaXZE2aAMQ==
+=ZeAz
+-----END PGP PUBLIC KEY BLOCK-----`;
+
 function versionSpecificTests() {
   it('Preferences of generated key', function() {
     const testPref = function(key) {
@@ -2891,7 +2942,7 @@ module.exports = () => describe('Key', function() {
     })).to.be.fulfilled;
   });
 
-  it('Method getExpirationTime V4 Key', async function() {
+  it('Key.getExpirationTime()', async function() {
     const [, pubKey] = await openpgp.readKeys({ armoredKeys: twoKeys });
     expect(pubKey).to.exist;
     expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
@@ -2899,7 +2950,7 @@ module.exports = () => describe('Key', function() {
     expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
   });
 
-  it('Method getExpirationTime expired V4 Key', async function() {
+  it('Key.getExpirationTime() - expired key', async function() {
     const pubKey = await openpgp.readKey({ armoredKey: expiredKey });
     expect(pubKey).to.exist;
     expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
@@ -2907,7 +2958,7 @@ module.exports = () => describe('Key', function() {
     expect(expirationTime.toISOString()).to.be.equal('1970-01-01T00:22:18.000Z');
   });
 
-  it('Method getExpirationTime V4 Subkey', async function() {
+  it('SubKey.getExpirationTime()', async function() {
     const [, pubKey] = await openpgp.readKeys({ armoredKeys: twoKeys });
     expect(pubKey).to.exist;
     expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
@@ -2915,34 +2966,22 @@ module.exports = () => describe('Key', function() {
     expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
   });
 
-  it('Method getExpirationTime V4 Key with capabilities', async function() {
+  it('Key.getExpirationTime() - never expiring key', async function() {
     const { minRSABits } = openpgp.config;
     try {
       openpgp.config.minRSABits = 1024;
       const privKey = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
-      privKey.users[0].selfCertifications[0].keyFlags = [1];
       const expirationTime = await privKey.getExpirationTime();
       expect(expirationTime).to.equal(Infinity);
-      const encryptExpirationTime = await privKey.getExpirationTime('encrypt_sign');
-      expect(encryptExpirationTime.toISOString()).to.equal('2008-02-12T17:12:08.000Z');
     } finally {
       openpgp.config.minRSABits = minRSABits;
     }
-
   });
 
-  it('Method getExpirationTime V4 Key with capabilities - capable primary key', async function() {
-    const { minRSABits } = openpgp.config;
-    try {
-      openpgp.config.minRSABits = 1024;
-      const privKey = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
-      const expirationTime = await privKey.getExpirationTime();
-      expect(expirationTime).to.equal(Infinity);
-      const encryptExpirationTime = await privKey.getExpirationTime('encrypt_sign');
-      expect(encryptExpirationTime).to.equal(Infinity);
-    } finally {
-      openpgp.config.minRSABits = minRSABits;
-    }
+  it('Key.getExpirationTime() - key expiration in direct-key signature', async function() {
+    const privKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature });
+    const expirationTime = await privKey.getExpirationTime();
+    expect(expirationTime.toISOString()).to.equal('2020-06-13T14:57:14.000Z');
   });
 
   it("decryptKey() - throw if key parameters don't correspond", async function() {
diff --git a/test/general/openpgp.js b/test/general/openpgp.js
index afb406ab..41d16a79 100644
--- a/test/general/openpgp.js
+++ b/test/general/openpgp.js
@@ -781,6 +781,57 @@ EplqEakMckCtikEnpxYe
 =b2Ln
 -----END PGP PUBLIC KEY BLOCK-----`;
 
+const expiredPublicKeyThroughDirectSignature = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+Be4ubVrj5KjhX2PVNEJd3XZRzaXZE2aAMQ==
+=ZeAz
+-----END PGP PUBLIC KEY BLOCK-----`;
+
 function withCompression(tests) {
   const compressionTypes = Object.keys(openpgp.enums.compression).map(k => openpgp.enums.compression[k]);
 
@@ -1541,6 +1592,15 @@ aOU=
     });
   });
 
+  describe('encrypt - unit tests', function() {
+    it('Does not encrypt to expired key (expiration time subpacket on a direct-key signature)', async function() {
+      const expiredKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature });
+      await expect(
+        openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey })
+      ).to.be.rejectedWith(/Primary key is expired/);
+    });
+  });
+
   describe('encrypt, decrypt, sign, verify - integration tests', function() {
     let privateKey_2000_2008;
     let publicKey_2000_2008;
diff --git a/test/general/signature.js b/test/general/signature.js
index 57470dc1..376df8d6 100644
--- a/test/general/signature.js
+++ b/test/general/signature.js
@@ -685,6 +685,14 @@ kCNcH9WI6idSzFjuYegECf+ZA1xOCjS9oLTGbSeT7jNfC8dH5+E92qlBLq4Ctt7k
     expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
   });
 
+  it('Consider signature expired at the expiration time', async function() {
+    const key = await openpgp.readKey({ armoredKey: keyExpiredBindingSig });
+    const { embeddedSignature } = key.subkeys[0].bindingSignatures[0];
+    expect(embeddedSignature.isExpired(embeddedSignature.created)).to.be.false;
+    expect(embeddedSignature.isExpired(new Date(embeddedSignature.getExpirationTime() - 1))).to.be.false;
+    expect(embeddedSignature.isExpired(embeddedSignature.getExpirationTime())).to.be.true;
+  });
+
   it('Signing fails if primary key is expired', async function() {
     const armoredExpiredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----