diff --git a/doc/configuration.rst b/doc/configuration.rst
index e15e9df05380ee3af0b297df7a234b602a9f0e42..b2fe0da8d15b06f3547d3d4c81a68fbac2a67368 100644
--- a/doc/configuration.rst
+++ b/doc/configuration.rst
@@ -293,7 +293,6 @@ only and there are some limitations:
   (even for verification only).
 * There cannot be more than eight keys per zone. Keys which are not
   published are not included in this number.
-* Single-Type Signing Scheme is not supported.
 
 Example how to generate NSEC3 capable zone signing key (ZSK) and key
 signing key (KSK) for zone ``example.com``::
@@ -325,8 +324,9 @@ The signing process consists of the following steps:
   for any keys that are present in keydir, but missing in zone file.
 * Removing expired signatures, invalid signatures, signatures expiring
   in a short time, and signatures with unknown key.
-* Creating any missing signatures. ``DNSKEY`` records are signed by
-  both ZSK and KSK keys, other records are signed only by ZSK keys.
+* Creating missing signatures. Unless the Single-Type Signing Scheme
+  is used, ``DNSKEY`` records in a zone apex are signed by KSK keys and
+  all other records are signed by ZSK keys.
 * SOA record is updated and resigned if any changes were performed.
 
 The zone signing is performed when the zone is loaded into server, on
diff --git a/src/knot/dnssec/zone-keys.c b/src/knot/dnssec/zone-keys.c
index f67f6c3db46e12bd4346a11e93b8c1d5af75bc5c..33c97135fb65ad9476c598dbde8a63e4ae242132 100644
--- a/src/knot/dnssec/zone-keys.c
+++ b/src/knot/dnssec/zone-keys.c
@@ -98,6 +98,7 @@ static void set_zone_key_flags(const knot_key_params_t *params,
 	key->next_event = next_event;
 
 	key->is_ksk = params->flags & KNOT_RDATA_DNSKEY_FLAG_KSK;
+	key->is_zsk = !key->is_ksk;
 
 	key->is_active = params->time_activate <= now &&
 	                 (params->time_inactive == 0 || now < params->time_inactive);
@@ -106,6 +107,36 @@ static void set_zone_key_flags(const knot_key_params_t *params,
 	                 (params->time_delete == 0 || now < params->time_delete);
 }
 
+/*!
+ * \brief Enable STSS if all keys are KSK/ZSK exclusively.
+ *
+ * \return STSS was enabled.
+ */
+static bool enable_single_type_signing(knot_zone_keys_t *keys)
+{
+	assert(keys);
+
+	int num_keys = 0;
+	int num_zone = 0;
+
+	knot_zone_key_t *key = NULL;
+	WALK_LIST(key, keys->list) {
+		if (key->is_ksk) { num_keys += 1; }
+		if (key->is_zsk) { num_zone += 1; }
+	}
+
+	if ((num_keys + num_zone == 0) || (num_keys > 0 && num_zone > 0)) {
+		return false;
+	}
+
+	WALK_LIST(key, keys->list) {
+		key->is_ksk = true;
+		key->is_zsk = true;
+	}
+
+	return true;
+}
+
 /*!
  * \brief Check if there is a functional KSK and ZSK for each used algorithm.
  */
@@ -134,11 +165,8 @@ static int check_keys_validity(const knot_zone_keys_t *keys)
 
 			// need fully enabled ZSK and KSK for each algorithm
 			if (key->is_active) {
-				if (key->is_ksk) {
-					algorithms[a].ksk_enabled = true;
-				} else {
-					algorithms[a].zsk_enabled = true;
-				}
+				if (key->is_ksk) { algorithms[a].ksk_enabled = true; }
+				if (key->is_zsk) { algorithms[a].zsk_enabled = true; }
 			}
 		}
 	}
@@ -283,6 +311,10 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name,
 
 	closedir(keydir);
 
+	if (enable_single_type_signing(keys)) {
+		log_zone_info(zone_name, "DNSSEC, using Single-Type Signing Scheme");
+	}
+
 	if (result == KNOT_EOK) {
 		result = check_keys_validity(keys);
 	}
diff --git a/src/knot/dnssec/zone-keys.h b/src/knot/dnssec/zone-keys.h
index e1c22e2e72411a8362c277f1b01e28ee241c88b4..ff40be60d92f0a5968f787917d48a00ed2017a31 100644
--- a/src/knot/dnssec/zone-keys.h
+++ b/src/knot/dnssec/zone-keys.h
@@ -40,7 +40,8 @@ typedef struct {
 	knot_dnssec_key_t dnssec_key;
 	knot_dnssec_sign_context_t *context;
 	uint32_t next_event;                 //!< Timestamp of next key event.
-	bool is_ksk;                         //!< Is KSK key.
+	bool is_ksk;                         //!< Is key-signing.
+	bool is_zsk;                         //!< Is zone-signing.
 	bool is_public;                      //!< Currently in zone.
 	bool is_active;                      //!< Currently used for signing.
 } knot_zone_key_t;
diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c
index 63438f9207dea4b5b6ddb178f61cfe64be9c7a0e..75c5572c228623e229528a73de5bfd55491d95df 100644
--- a/src/knot/dnssec/zone-sign.c
+++ b/src/knot/dnssec/zone-sign.c
@@ -110,18 +110,10 @@ static bool use_key(const knot_zone_key_t *key, const knot_rrset_t *covered)
 		return false;
 	}
 
-	if (key->is_ksk) {
-		if (covered->type != KNOT_RRTYPE_DNSKEY) {
-			return false;
-		}
+	bool is_zone_key = covered->type == KNOT_RRTYPE_DNSKEY &&
+	                   knot_dname_is_equal(key->dnssec_key.name, covered->owner);
 
-		// use KSK only in the zone apex
-		if (!knot_dname_is_equal(key->dnssec_key.name, covered->owner)) {
-			return false;
-		}
-	}
-
-	return true;
+	return (key->is_ksk && is_zone_key) || (key->is_zsk && !is_zone_key);
 }
 
 /*!
diff --git a/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh b/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh
index cfd52b0ec02b745eeec14c7939865b0b4bd47844..b6db1f0314344601f1c8896b1ed9d32f3e372a3d 100755
--- a/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh
+++ b/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh
@@ -34,6 +34,14 @@ keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_roll_ok
 keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_roll_ok
 keygen -a ECDSAP256SHA256 -P $TIME_FUTURE -A $TIME_PAST rsa_ecdsa_roll_ok
 
+#
+# valid single-type signing scheme scenarios
+#
+
+keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_stss_ksk
+
+keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST rsa_stss_zsk
+
 #
 # invalid scenarios
 #
@@ -53,9 +61,6 @@ keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_FUTURE rsa_inactive_zsk
 keygen -a RSASHA256 -b 2048 -P $TIME_FUTURE -A $TIME_FUTURE -f KSK rsa_no_zsk
 keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_no_zsk
 
-keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_twice_ksk
-keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_twice_ksk
-
 keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ksk_only
 keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ksk_only
 keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ksk_only
diff --git a/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz b/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz
index 25aef643afd6f39ff8f00ea290eb7016b4300bac..0161a309a833c4628c036bd6f4d0a880e840406e 100644
Binary files a/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz and b/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz differ
diff --git a/tests-extra/tests/dnssec/dnskey_algorithms/test.py b/tests-extra/tests/dnssec/dnskey_algorithms/test.py
index 20b81d4c4c10535f0cfb2c377971067a3fa8c736..b1ad20de210c755d7d0702f60644459e709dcde7 100644
--- a/tests-extra/tests/dnssec/dnskey_algorithms/test.py
+++ b/tests-extra/tests/dnssec/dnskey_algorithms/test.py
@@ -14,13 +14,15 @@ TEST_CASES = {
     "rsa_ok":             True,
     "rsa_ecdsa_ok":       True,
     "rsa_ecdsa_roll_ok":  True,
+    # valid single-type signing
+    "rsa_stss_ksk":       True,
+    "rsa_stss_zsk":       True,
     # invalid cases
     "rsa_future_all":     False,
     "rsa_future_publish": False,
     "rsa_future_active":  False,
     "rsa_inactive_zsk":   False,
     "rsa_no_zsk":         False,
-    "rsa_twice_ksk":      False,
     "rsa_ecdsa_ksk_only": False,
     "rsa256_rsa512":      False,
 }
diff --git a/tests-extra/tests/dnssec/single_type_signing/test.py b/tests-extra/tests/dnssec/single_type_signing/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..06b354240b5b11c362b1571a7917adea80a2c3b7
--- /dev/null
+++ b/tests-extra/tests/dnssec/single_type_signing/test.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+"""
+DNSSEC Single-Type Signing Scheme, RFC 6781
+"""
+from dnstest.utils import *
+from dnstest.test import Test
+
+t = Test()
+
+knot = t.server("knot")
+zones = t.zone_rnd(3, dnssec=False, records=10)
+t.link(zones, knot)
+t.start()
+
+# one KSK
+knot.gen_key(zones[0], ksk=True, alg="RSASHA256", key_len="512")
+
+# one ZSK
+knot.gen_key(zones[1], ksk=False, alg="RSASHA512", key_len="1024")
+
+# multiple KSKs
+knot.gen_key(zones[2], ksk=True, alg="RSASHA512", key_len="1024")
+knot.gen_key(zones[2], ksk=True, alg="RSASHA256", key_len="512")
+
+knot.dnssec_enable = True
+knot.gen_confile()
+knot.reload()
+t.sleep(2)
+knot.flush()
+t.sleep(2)
+knot.stop()
+
+for zone in zones:
+    knot.zone_verify(zone)
+
+t.end()
diff --git a/tests-extra/tools/dnstest/zonefile.py b/tests-extra/tools/dnstest/zonefile.py
index 19c6f45e374ac055bfbc1174ccc25c16687f1bde..aa894c7562abd402118a03ae6a5ed1ce996c8ab5 100644
--- a/tests-extra/tools/dnstest/zonefile.py
+++ b/tests-extra/tools/dnstest/zonefile.py
@@ -118,7 +118,7 @@ class ZoneFile(object):
 
         # note: convert origin to lower case due to a bug in dnssec-verify
         origin = self.name.lower()
-        cmd = Popen(["dnssec-verify", "-o", origin, self.path],
+        cmd = Popen(["dnssec-verify", "-z", "-o", origin, self.path],
                     stdout=PIPE, stderr=PIPE, universal_newlines=True)
         (out, err) = cmd.communicate()