diff --git a/samples/knot.full.conf b/samples/knot.full.conf
index 230ea92ee4b89b0ea31daf3c629d6510ef66074d..e2fc15cb54f8dd38413d56b1376ab1656b3bafb5 100644
--- a/samples/knot.full.conf
+++ b/samples/knot.full.conf
@@ -23,10 +23,14 @@ system {
   # Working directory of the server
   # Used to store compiled zones and PID file
   storage "/tmp/knot-sample";
+}
+
+# Section 'keys' contains list of TSIG keys
+keys {
 
   # TSIG key
   #
-  # format: key key-type "<key>";
+  # format: name key-type "<key>";
   # where key-type may be one of the following:
   #   hmac-md5
   #   hmac-sha1
@@ -35,8 +39,10 @@ system {
   #   hmac-sha384
   #   hmac-sha512
   # and <key> is the private key
-  key hmac-md5 "Wg==";
+  key0.server0 hmac-md5 "Wg==";
 
+  # TSIG key for zone
+  key0.example.com hmac-md5 "==gW";
 }
 
 # Section 'interfaces' contains definitions of listening interfaces.
@@ -77,6 +83,7 @@ remotes {
   server0 {             # <name> is an arbitrary symbolic name
     address 127.0.0.1;  # <address> may be ither IPv4 or IPv6 address
     port 53531;         # port is optional (default: 53)
+    key key0.server0;    # (optional) specification of TSIG key associated for this remote
   }
 
   # Format 2: <name> { address <address>@<port>; }
@@ -144,6 +151,10 @@ zones {
     # Default value: 5
     notify-retries 5;
 
+    # TSIG key associated with this zone (optional)
+    # Keys associated with remote override keys associated with zone
+    key key0.example.com;
+
     # Timeout for syncing changes from zone database to zonefile
     # Possible values: <1..INT_MAX> (seconds)
     # Default value: inherited from zones.zonefile-sync
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index fb7637e9c156d62592c3644d7410da6570ba6cbb..8d965427f2bbf824a59dd6462cd8d43373523be3 100644
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -58,6 +58,7 @@ identity        { lval.t = yytext; return IDENTITY; }
 version         { lval.t = yytext; return VERSION; }
 storage         { lval.t = yytext; return STORAGE; }
 key             { lval.t = yytext; return KEY; }
+keys            { lval.t = yytext; return KEYS; }
 remotes         { lval.t = yytext; return REMOTES; }
 
 zones           { lval.t = yytext; return ZONES; }
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index 43cbaa04a7f506672c8124a83ee73c33e3587908..87c36d4ece4e15d0c3a6a9ac30e157be2a8228ac 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -60,7 +60,7 @@ static void conf_acl_item(void *scanner, char *item)
 
       /* Append to list if found. */
      if (!found) {
-	char buf[256];
+	char buf[512];
 	snprintf(buf, sizeof(buf), "remote '%s' is not defined", item);
 	cf_error(scanner, buf);
      } else {
@@ -87,6 +87,22 @@ static void conf_acl_item(void *scanner, char *item)
      free(item);
    }
 
+static void conf_key_item(void *scanner, conf_key_t **key, char *item)
+{
+    /* Find existing node in keys. */
+    conf_key_t* r = 0;
+    WALK_LIST (r, new_config->keys) {
+	if (strcmp(r->name, item) == 0) {
+	    *key = r;
+	    return;
+	}
+    }
+
+    char buf[512];
+    snprintf(buf, sizeof(buf), "key '%s' is not defined", item);
+    cf_error(scanner, buf);
+}
+
 %}
 
 %pure-parser
@@ -110,7 +126,7 @@ static void conf_acl_item(void *scanner, char *item)
 %token <tok> SIZE
 %token <tok> BOOL
 
-%token <tok> SYSTEM IDENTITY VERSION STORAGE KEY
+%token <tok> SYSTEM IDENTITY VERSION STORAGE KEY KEYS
 %token <tok> TSIG_ALGO_NAME
 
 %token <tok> REMOTES
@@ -222,11 +238,24 @@ system:
  | system IDENTITY TEXT ';' { new_config->identity = $3.t; }
  | system STORAGE TEXT ';' { new_config->storage = $3.t; }
  | system KEY TSIG_ALGO_NAME TEXT ';' {
-     new_config->key.algorithm = $3.alg;
-     new_config->key.secret = $4.t;
-   }
+     fprintf(stderr, "warning: Config option 'system.key' is deprecated "
+		     "and has no effect.\n");
+     free($4.t);
+ }
  ;
 
+keys:
+   KEYS '{'
+ | keys TEXT TSIG_ALGO_NAME TEXT ';' {
+     conf_key_t *k = malloc(sizeof(conf_key_t));
+     memset(k, 0, sizeof(conf_key_t));
+     k->name = $2.t;
+     k->algorithm = $3.alg;
+     k->secret = $4.t;
+     add_tail(&new_config->keys, &k->n);
+     ++new_config->key_count;
+}
+
 remote_start:
  | TEXT { conf_start_remote($1.t); }
  | LOG_SRC  { conf_start_remote(strdup($1.t)); }
@@ -285,6 +314,14 @@ remote:
        }
      }
    }
+  | remote KEY TEXT ';' {
+      if (this_remote->key != 0) {
+	cf_error(scanner, "only one key definition is allowed in remote section\n");
+      } else {
+	conf_key_item(scanner, &this_remote->key, $3.t);
+	free($3.t);
+      }
+  }
  ;
 
 remotes:
@@ -422,6 +459,14 @@ zone:
 	   this_zone->notify_timeout = $3.i;
        }
    }
+ | zone KEY TEXT ';' {
+     if (this_zone->key != 0) {
+       cf_error(scanner, "only one key definition is allowed in zone section\n");
+     } else {
+       conf_key_item(scanner, &this_zone->key, $3.t);
+       free($3.t);
+     }
+   }
  ;
 
 zones:
@@ -537,7 +582,7 @@ log_start:
 log: LOG '{' log_start log_end;
 
 
-conf: ';' | system '}' | interfaces '}' | remotes '}' | zones '}' | log '}';
+conf: ';' | system '}' | interfaces '}' | keys '}' | remotes '}' | zones '}' | log '}';
 
 %%
 
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index a3025ed54e2e686b1ccad01876295cb9cdc65430..53584262a6a27656199dea70cacdbecb1a469e00 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -81,6 +81,18 @@ void cf_error(void *scanner, const char *msg)
  * Config helper functions.
  */
 
+/*! \brief Free TSIG key. */
+static void key_free(conf_key_t *k)
+{
+	/* Secure erase. */
+	if (k->secret) {
+		memset(k->secret, 0, strlen(k->secret));
+	}
+	free(k->secret);
+	free(k->name);
+	free(k);
+}
+
 /*! \brief Free config interfaces. */
 static void iface_free(conf_iface_t *iface)
 {
@@ -396,6 +408,7 @@ conf_t *conf_new(const char* path)
 	init_list(&c->zones);
 	init_list(&c->hooks);
 	init_list(&c->remotes);
+	init_list(&c->keys);
 
 	// Defaults
 	c->zone_checks = 0;
@@ -478,11 +491,10 @@ void conf_truncate(conf_t *conf, int unload_hooks)
 		init_list(&conf->hooks);
 	}
 
-	// Free key
-	if (conf->key.secret) {
-		free(conf->key.secret);
+	// Free keys
+	WALK_LIST_DELSAFE(n, nxt, conf->keys) {
+		key_free((conf_key_t *)n);
 	}
-	memset(&conf->key, 0, sizeof(conf_key_t));
 
 	// Free interfaces
 	WALK_LIST_DELSAFE(n, nxt, conf->ifaces) {
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index da639ec5a45ace84da8e4436fae07c65c22faf34..5a479c605d063dee96ba279267eae58d4e1b5576 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -28,6 +28,32 @@
 #define CONFIG_NOTIFY_TIMEOUT 60 /*!< 60s (suggested in RFC1996) */
 #define CONFIG_DBSYNC_TIMEOUT (60*60) /*!< 1 hour. */
 
+/*!
+ * \brief List of TSIG algoritms.
+ *
+ * Master list of TSIG algoritms as per IANA registry
+ * http://www.iana.org/assignments/tsig-algorithm-names/tsig-algorithm-names.xhtml
+ */
+typedef enum tsig_alg_t {
+	GSS_TSIG,
+	HMAC_MD5,
+	HMAC_SHA1,
+	HMAC_SHA224,
+	HMAC_SHA256,
+	HMAC_SHA384,
+	HMAC_SHA512
+} tsig_alg_t;
+
+/*!
+ * \brief Configuration for the TSIG key.
+ */
+typedef struct conf_key_t {
+	node n;	              /*!< List node. */
+	char *name;           /*!< Key name. */
+	tsig_alg_t algorithm; /*!< Key algorithm.  */
+	char *secret;         /*!< Key data. */
+} conf_key_t;
+
 /*!
  * \brief Configuration for the interface
  *
@@ -41,6 +67,7 @@ typedef struct conf_iface_t {
 	char *address;    /*!< IP (IPv4/v6) address for this interface */
 	int   port;       /*!< Port number for this interface */
 	int family;       /*!< Address family. */
+	conf_key_t *key;  /*!< TSIG key (remotes only). */
 } conf_iface_t;
 
 /*!
@@ -53,30 +80,6 @@ typedef struct conf_remote_t {
 	conf_iface_t *remote; /*!< Pointer to interface descriptor. */
 } conf_remote_t;
 
-/*!
- * \brief List of TSIG algoritms.
- *
- * Master list of TSIG algoritms as per IANA registry
- * http://www.iana.org/assignments/tsig-algorithm-names/tsig-algorithm-names.xhtml
- */
-typedef enum tsig_alg_t {
-	GSS_TSIG,
-	HMAC_MD5,
-	HMAC_SHA1,
-	HMAC_SHA224,
-	HMAC_SHA256,
-	HMAC_SHA384,
-	HMAC_SHA512
-} tsig_alg_t;
-
-/*!
- * \brief Configuration for the TSIG key.
- */
-typedef struct conf_key_t {
-	tsig_alg_t algorithm; /*!< Key algorithm.  */
-	char *secret;         /*!< Key data. */
-} conf_key_t;
-
 /*!
  * \brief Zone configuration.
  *
@@ -91,15 +94,16 @@ typedef struct conf_key_t {
 typedef struct conf_zone_t {
 	node n;
 	char *name;               /*!< Zone name. */
-	enum knot_rr_class cls; /*!< Zone class (IN or CH). */
+	enum knot_rr_class cls;   /*!< Zone class (IN or CH). */
 	char *file;               /*!< Path to a zone file. */
 	char *db;                 /*!< Path to a database file. */
 	char *ixfr_db;            /*!< Path to a IXFR database file. */
-	size_t ixfr_fslimit;         /*!< File size limit for IXFR journal. */
+	size_t ixfr_fslimit;      /*!< File size limit for IXFR journal. */
 	int dbsync_timeout;       /*!< Interval between syncing to zonefile.*/
 	int enable_checks;        /*!< Semantic checks for parser.*/
 	int notify_retries;       /*!< NOTIFY query retries. */
 	int notify_timeout;       /*!< Timeout for NOTIFY response (s). */
+	conf_key_t *key;          /*!< TSIG key for zone. */
 	struct {
 		list xfr_in;      /*!< Remotes accepted for for xfr-in.*/
 		list xfr_out;     /*!< Remotes accepted for xfr-out.*/
@@ -152,7 +156,6 @@ typedef struct conf_t {
 	char *version;  /*!< Version for CH TXT version.{bind|server} */
 	char *storage;  /*!< Persistent storage path for databases and such. */
 	char *pidfile;  /*!< PID file path. */
-	conf_key_t key; /*!< Server TSIG key. */
 
 	/*
 	 * Log
@@ -167,7 +170,13 @@ typedef struct conf_t {
 	int ifaces_count; /*!< Count of interfaces. */
 
 	/*
-	 * Remotse
+	 * TSIG keys
+	 */
+	list keys;     /*!< List of TSIG keys. */
+	int key_count; /*!< Count of TSIG keys. */
+
+	/*
+	 * Remotes
 	 */
 	list remotes;     /*!< List of remotes. */
 	int remotes_count;/*!< Count of remotes. */
diff --git a/src/tests/files/sample_conf b/src/tests/files/sample_conf
index d7d54b4fa8f070a44f3918f419c4d55db4e935e7..ca7350c8143159edc37c5636237d3c38d1f1ad98 100644
--- a/src/tests/files/sample_conf
+++ b/src/tests/files/sample_conf
@@ -9,21 +9,26 @@ system {
   identity "I have no mouth and must scream";
   version "Infinitesimal";
   storage "/var/run/knot/";
-  key hmac-md5 "Wg==";
+}
+
+keys {
+  key0 hmac-md5 "Wg=="; # key special for one remote
+  key1 hmac-md5 "==gW"; # implicit key for whole zone
+}
 
+remotes {
+  remote0 { address 1.2.3.4; key key0; }
 }
 
 zones {
   example.net {
     file "/var/lib/knot/example.net";
+    xfr-out remote0;
+    key key1;
   }
 }
 
 interfaces {
-  # short notation
-  # name address@port
-  # interface0 10.10.1.1@53;
-  # will expand into long notation
   interface0 {
     address 10.10.1.1;
     port 53531;
diff --git a/src/tests/knot/conf_tests.c b/src/tests/knot/conf_tests.c
index 6f3443574e4ceb5761f7281f58640601d023acee..5a15d262779620a9de180cfa0ea702e72762dd7e 100644
--- a/src/tests/knot/conf_tests.c
+++ b/src/tests/knot/conf_tests.c
@@ -22,7 +22,7 @@ unit_api conf_tests_api = {
  */
 static int conf_tests_count(int argc, char *argv[])
 {
-	return 20;
+	return 22;
 }
 
 /*! Run all scheduled tests for given parameters.
@@ -58,8 +58,14 @@ static int conf_tests_run(int argc, char *argv[])
 	cmp_ok(iface->port, "==", 53, "interface1 default port check");
 
 	// Test 9,10: Check server key
-	cmp_ok(conf->key.algorithm, "==", HMAC_MD5, "TSIG key algorithm check");
-	is(conf->key.secret, "Wg==", "TSIG key secret check");
+	if(conf->key_count <= 0) {
+		ok(0, "TSIG key algorithm check - NO KEY FOUND");
+		ok(0, "TSIG key secret check - NO KEY FOUND");
+	} else {
+		conf_key_t *k = (conf_key_t *)HEAD(conf->keys);
+		cmp_ok(k->algorithm, "==", HMAC_MD5, "TSIG key algorithm check");
+		is(k->secret, "Wg==", "TSIG key secret check");
+	}
 
 	// Test 11,12,13,14,15,16,17,18: Check logging facilities
 	cmp_ok(conf->logs_count, "==", 4, "log facilites count check");
@@ -98,6 +104,11 @@ static int conf_tests_run(int argc, char *argv[])
 	}
 	endskip;
 
+	// Test 21,22: remote,zone -> key binding
+	conf_key_t *k = ((conf_iface_t*)HEAD(conf->remotes))->key;
+	is(k ? k->name : "", "key0", "remote is connected with correct key");
+	k = ((conf_zone_t*)HEAD(conf->zones))->key;
+	is(k ? k->name : "", "key1", "zone is connected with correct key");
 	}
 	endskip;