]> granicus.if.org Git - pdns/commitdiff
* Make everything aware of multiple simultaneous signing keys
authorBert Hubert <bert.hubert@netherlabs.nl>
Sun, 9 Jan 2011 08:58:55 +0000 (08:58 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Sun, 9 Jan 2011 08:58:55 +0000 (08:58 +0000)
        * Remove APIs that contravene this
* Rename SHA1-centric functioncalls: s/SHA1/Hash/g
* Diagnose the sillines of getSignerApexFor which rediscovers the right key
  to use..
        * no fix yet
* If no ZSKs, use active KSKs for signing (allowing single-key operation)
* Fix up signature caching which assumed keytag = key identity
* Only sign the DNSKEY RRSET with active KSKs from now on
* Make secure-zone run rectify-zone
* Remove --force from secure-zone (silly)
* Make RSASHA256 default for secure-zone

git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1843 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/dbdnsseckeeper.cc
pdns/dnssecinfra.cc
pdns/dnssecinfra.hh
pdns/dnsseckeeper.hh
pdns/packethandler.cc
pdns/pdnssec.cc

index 904847e4e7004f9b206d64c17a5ada6546e53c94..609c362b0796dd5d16f617bfe61d54d141bc55de 100644 (file)
@@ -19,15 +19,17 @@ namespace fs = boost::filesystem;
 using namespace std;
 using namespace boost;
 
-bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* dpk
+bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone) 
 {
   keyset_t keys = getKeys(zone, true);
   // need to get an *active* one!
   //cerr<<__FUNCTION__<<"Got "<<keys.size()<<" keys"<<endl;
-  if(dpk && !keys.empty()) {
-    *dpk = keys.begin()->first;
+  BOOST_FOREACH(keyset_t::value_type& val, keys) {
+    if(val.second.active) {
+      return true;
+    }
   }
-  return !keys.empty();
+  return false;
 }
 
 
@@ -179,133 +181,126 @@ void DNSSECKeeper::secureZone(const std::string& name, int algorithm)
 {
   addKey(name, true, algorithm);
 }
-bool getSignerFor(DNSSECKeeper& dk, const std::string& qname, std::string &signer, uint8_t& algorithm)
+
+// nobody should ever call this function, you know the SOA/auth already!
+bool getSignerApexFor(DNSSECKeeper& dk, const std::string& qname, std::string &signer)
 {
+  // cerr<<"getSignerApexFor: called, and should not be, should go away!"<<endl;
   signer=qname;
-  DNSSECPrivateKey dpk;
   do {
-    if(dk.haveActiveKSKFor(signer, &dpk)) {
-      algorithm = dpk.d_algorithm;
+    if(dk.haveActiveKSKFor(signer)) {
       return true;
     }
   } while(chopOff(signer));
   return false;
 }
 
-// this should be able to answer with multiple keys, in case of multiple active ZSKs XXX
-DNSKEYRecordContent getDNSKEYFor(DNSSECKeeper& dk, const std::string& qname, bool withKSK, RSAContext* rc)
-{
-  // cerr<<"Asked for a DNSKEY for '"<<qname<<"', withKSK="<<withKSK<<"\n";
-  DNSSECPrivateKey dpk;
-
-  if(!withKSK) {
-    DNSSECKeeper::keyset_t zskset=dk.getKeys(qname, false);
-    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
-      if(value.second.active) {
-     //   cerr<<"Found a ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl;
-        *rc=value.first.d_key;
-        return value.first.getDNSKEY();
-      }
-      else 
-        cerr<<"Found an inactive ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl;
-    }
-    cerr<<"Could not find an active ZSK for '"<<qname<<"'"<<endl;
-    exit(1);
-  }
-  else if(dk.haveActiveKSKFor(qname, &dpk)) {
-//    cerr<<"Found a KSK for '"<<qname<<"'"<<endl;
-    *rc=dpk.d_key;
-    return dpk.getDNSKEY();
-  } else {
-      cerr<<"DID NOT FIND A ZSK for '"<<qname<<"'"<<endl;
-      exit(1);
-  }
-}
-
-int getRRSIGForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL, 
-                    vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent& rrc, bool ksk)
+/* this is where the RRSIG gets filled out, the hashing gets done, key apex *name* gets found,
+   but the actual signing happens in fillOutRRSIG */
+int getRRSIGsForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL, 
+                    vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs, bool ksk)
 {
   if(toSign.empty())
     return -1;
-
+  RRSIGRecordContent rrc;
   rrc.d_type=signQType;
 
-  // d_algorithm gets filled out by fillOutRRSIG, since it  gets the key
+  // d_algorithm gets filled out by getSignerAPEX, since only it looks up the key
   rrc.d_labels=countLabels(signQName); 
   rrc.d_originalttl=signTTL; 
   rrc.d_siginception=getCurrentInception();;
   rrc.d_sigexpire = rrc.d_siginception + 14*86400; // XXX should come from zone metadata
+  rrc.d_tag = 0;
   
-  rrc.d_tag=0;
-  if(!getSignerFor(dk, signQName, rrc.d_signer, rrc.d_algorithm)) {
+  // XXX we know the apex already.. is is the SOA name which we determined earlier
+  if(!getSignerApexFor(dk, signQName, rrc.d_signer)) {
     cerr<<"No signer known for '"<<signQName<<"'\n";
     return -1;
   }
-    
-  string hash= getSHA1HashForRRSET(signQName,  rrc, toSign);
-  fillOutRRSIG(dk, signQName, rrc, hash, toSign, ksk);
+  // we sign the RRSET in toSign + the rrc w/o key
+  
+  DNSSECKeeper::keyset_t keys = dk.getKeys(rrc.d_signer);
+  vector<DNSSECPrivateKey> KSKs, ZSKs;
+  vector<DNSSECPrivateKey>* signingKeys;
+  
+  // if ksk==1, only get KSKs
+  // if ksk==0, get ZSKs, unless there is no ZSK, then get KSK
+  BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type& keymeta, keys) {
+    if(!keymeta.second.active) 
+      continue;
+      
+    if(keymeta.second.keyOrZone)
+      KSKs.push_back(keymeta.first);
+    else if(!ksk)
+      ZSKs.push_back(keymeta.first);
+  }
+  if(ksk)
+    signingKeys = &KSKs;
+  else {
+    if(ZSKs.empty())
+      signingKeys = &KSKs;
+    else
+      signingKeys =&ZSKs;
+  }
+  
+  BOOST_FOREACH(DNSSECPrivateKey& dpk, *signingKeys) {
+    fillOutRRSIG(dpk, signQName, rrc, toSign);
+    rrcs.push_back(rrc);
+  }
   return 0;
 }
 
+// this is the entrypoint from DNSPacket
 void addSignature(DNSSECKeeper& dk, const std::string signQName, const std::string& wildcardname, uint16_t signQType, 
   uint32_t signTTL, DNSPacketWriter::Place signPlace, 
   vector<shared_ptr<DNSRecordContent> >& toSign, uint16_t maxReplyLen, DNSPacketWriter& pw)
 {
   // cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
 
-  RRSIGRecordContent rrc;
+  vector<RRSIGRecordContent> rrcs;
   if(toSign.empty())
     return;
 
-  for(int ksk = 0; ksk < 2; ++ksk) {
-    if(getRRSIGForRRSET(dk, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrc, ksk) < 0) {
-      cerr<<"Error signing a record!"<<endl;
-      return;
-    }
-    
+  if(getRRSIGsForRRSET(dk, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrcs, signQType == QType::DNSKEY) < 0) {
+    cerr<<"Error signing a record!"<<endl;
+    return;
+  }
+  BOOST_FOREACH(RRSIGRecordContent& rrc, rrcs) {
     pw.startRecord(signQName, QType::RRSIG, 3600, 1, 
-    signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace); 
+      signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace); 
     rrc.toPacket(pw);
     if(maxReplyLen &&  (pw.size() + 20) > maxReplyLen) {
       pw.rollback();
       pw.getHeader()->tc=1;
       return;
     }
-    
-    pw.commit();
-    if(signQType != QType::DNSKEY)
-      break;
   }
+  pw.commit();
 
   toSign.clear();
 }
 
-pthread_mutex_t g_rrsigs_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t g_signatures_lock = PTHREAD_MUTEX_INITIALIZER;
+static map<pair<RSAContext, string>, string> g_signatures;
 
-map<pair<string, uint16_t>, RRSIGRecordContent> g_rrsigs;
-
-void fillOutRRSIG(DNSSECKeeper& dk, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK) 
+void fillOutRRSIG(DNSSECPrivateKey& dpk, const std::string& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign) 
 {
-  RSAContext rc;
-
-  DNSKEYRecordContent drc=getDNSKEYFor(dk, rrc.d_signer, withKSK, &rc);
+  DNSKEYRecordContent drc= dpk.getDNSKEY(); 
+  RSAContext& rc = dpk.d_key;
   rrc.d_tag = drc.getTag();
   rrc.d_algorithm = drc.d_algorithm;
+  string realhash=getHashForRRSET(signQName, rrc, toSign); // this is what we sign
 
-  {  
-    Lock l(&g_rrsigs_lock);
-    if(g_rrsigs.count(make_pair(hash, rrc.d_tag))) {
-      // cerr<<"RRSIG cache hit !"<<endl;
-      rrc = g_rrsigs[make_pair(hash, rrc.d_tag)];
+  unsigned char signature[mpi_size(&rc.getContext().N)];
+
+  {
+    Lock l(&g_signatures_lock);
+    if(g_signatures.count(make_pair(rc, realhash))) {
+      rrc.d_signature=g_signatures[make_pair(rc, realhash)];
       return;
     }
   }
-    
-  string realhash=getSHA1HashForRRSET(signQName, rrc, toSign);
-
-  unsigned char signature[mpi_size(&rc.getContext().N)];
-
+  
   int ret=rsa_pkcs1_sign(&rc.getContext(), RSA_PRIVATE, 
     rrc.d_algorithm < 8 ? SIG_RSA_SHA1 : SIG_RSA_SHA256, 
     rrc.d_algorithm < 8 ? 20 : 32,
@@ -317,7 +312,7 @@ void fillOutRRSIG(DNSSECKeeper& dk, const std::string& signQName, RRSIGRecordCon
   }
   
   rrc.d_signature.assign((char*)signature, sizeof(signature));
-  
-  Lock l(&g_rrsigs_lock);
-  g_rrsigs[make_pair(hash, rrc.d_tag)] = rrc;
+
+  Lock l(&g_signatures_lock);
+  g_signatures[make_pair(rc, realhash)] = rrc.d_signature;
 }
index 88d15275901c2c50495534f4ef7e64f0157d181f..2b8fc64fd1f3f7b88f33b27c5982062cd802af78 100644 (file)
@@ -224,18 +224,15 @@ bool sharedDNSSECCompare(const shared_ptr<DNSRecordContent>& a, const shared_ptr
   return a->serialize("", true, true) < b->serialize("", true, true);
 }
 
-string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords) 
+string getHashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords) 
 {
   sort(signRecords.begin(), signRecords.end(), sharedDNSSECCompare);
 
   string toHash;
   toHash.append(const_cast<RRSIGRecordContent&>(rrc).serialize("", true, true));
-  toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end;
-  //  cerr<<"toHash start size: "<<toHash.size()<<", signature length: "<<rrc.d_signature.length()<<endl;
-
+  toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end, don't sign the signature!
 
   BOOST_FOREACH(shared_ptr<DNSRecordContent>& add, signRecords) {
-    //  cerr<<"\t IN "<<rrc.d_originalttl<<"\t"<<add->getZoneRepresentation()<<"\n";
     toHash.append(toLower(simpleCompress(qname, "")));
     uint16_t tmp=htons(rrc.d_type);
     toHash.append((char*)&tmp, 2);
@@ -248,7 +245,6 @@ string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& r
     toHash.append((char*)&tmp, 2);
     toHash.append(rdata);
   }
-  //  cerr<<"toHash: "<<makeHexDump(toHash)<<endl;
   
   if(rrc.d_algorithm <= 7 ) {
     unsigned char hash[20];
index 324459b454a0a574f1708cfcf7f9fbcd20b62533..feb1ed93f8d90fd7f9c7f9d64151360b3a03f7ed 100644 (file)
@@ -27,7 +27,7 @@ DNSKEYRecordContent getRSAKeyFromISC(rsa_context* rsa, const char* fname);
 DNSKEYRecordContent getRSAKeyFromISCString(rsa_context* rsa, const std::string& content);
 void makeRSAPublicKeyFromDNS(rsa_context* rc, const DNSKEYRecordContent& dkrc);
 bool sharedDNSSECCompare(const boost::shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b);
-string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, std::vector<boost::shared_ptr<DNSRecordContent> >& signRecords);
+string getHashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, std::vector<boost::shared_ptr<DNSRecordContent> >& signRecords);
 DNSKEYRecordContent makeDNSKEYFromRSAKey(const rsa_context* rc, uint8_t algorithm, uint16_t flags);
 DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordContent& drc, int digest=1);
 
@@ -36,15 +36,15 @@ int countLabels(const std::string& signQName);
 
 class RSAContext;
 class DNSSECKeeper; 
+struct DNSSECPrivateKey;
 
-bool getSignerFor(DNSSECKeeper& dk, const std::string& keyrepodir, const std::string& qname, std::string &signer, uint8_t& algorithm);
-DNSKEYRecordContent getDNSKEYFor(DNSSECKeeper& dk, const std::string& keyrepodir, const std::string& qname, bool withKSK, RSAContext* rc);
-void fillOutRRSIG(DNSSECKeeper& dk, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK=false);
+bool getSignerApexFor(DNSSECKeeper& dk, const std::string& keyrepodir, const std::string& qname, std::string &signer);
+void fillOutRRSIG(DNSSECPrivateKey& dpk, const std::string& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign);
 uint32_t getCurrentInception();
 void addSignature(DNSSECKeeper& dk, const std::string signQName, const std::string& wildcardname, uint16_t signQType, uint32_t signTTL, DNSPacketWriter::Place signPlace, vector<shared_ptr<DNSRecordContent> >& toSign, 
   uint16_t maxReplyLength, DNSPacketWriter& pw);
-int getRRSIGForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL, 
-                    vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent &rrc, bool ksk);
+int getRRSIGsForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL, 
+                    vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent> &rrc, bool ksk);
 
 std::string hashQNameWithSalt(unsigned int times, const std::string& salt, const std::string& qname);
 
index 1d117e662b9e5e30e89ef698794590855bbc80cb..ad99fe3ec6af61d72131cb15714010ba5f2436c0 100644 (file)
 #define PDNSSEC_MC(x) PDNSSEC_MI(x); mpi_copy(&d_context.x, const_cast<mpi*>(&orig.d_context.x))
 #define PDNSSEC_MF(x) mpi_free(&d_context.x, 0)
 
+inline bool operator<(const mpi& a, const mpi& b)
+{
+  return mpi_cmp_mpi(&a, &b) < 0;
+}
+
 class RSAContext
 {
 public:
@@ -28,6 +33,12 @@ public:
     PDNSSEC_MF(E); PDNSSEC_MF(D); PDNSSEC_MF(P); PDNSSEC_MF(Q); PDNSSEC_MF(DP); PDNSSEC_MF(DQ); PDNSSEC_MF(QP); PDNSSEC_MF(RN); PDNSSEC_MF(RP); PDNSSEC_MF(RQ);
   }
 
+  bool operator<(const RSAContext& rhs) const
+  {
+    return tie(d_context.N, d_context.E, d_context.D, d_context.P, d_context.Q, d_context.DP, d_context.DQ, d_context.QP)
+    < tie(rhs.d_context.N, rhs.d_context.E, rhs.d_context.D, rhs.d_context.P, rhs.d_context.Q, rhs.d_context.DP, rhs.d_context.DQ, rhs.d_context.QP);
+  }
+
   RSAContext(const RSAContext& orig) 
   {
     d_context.ver = orig.d_context.ver;
@@ -74,7 +85,7 @@ private:
   rsa_context d_context;
 };
 
-// ?
+// see above
 #undef PDNSSEC_MC
 #undef PDNSSEC_MI
 #undef PDNSSEC_MF
@@ -104,7 +115,7 @@ private:
   UeberBackend d_db;
 public:
   DNSSECKeeper() : d_db("key-only"){}
-  bool haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* ksk=0);
+  bool haveActiveKSKFor(const std::string& zone);
   
   keyset_t getKeys(const std::string& zone, boost::tribool allOrKeyOrZone = boost::indeterminate);
   DNSSECPrivateKey getKeyById(const std::string& zone, unsigned int id);
index 4a7c7dff0efdf8253dda316f90e38d553e1e8253..915a67def8d96406a8f324da9cfe772355f2e2a9 100644 (file)
@@ -207,23 +207,13 @@ int PacketHandler::doDNSKEYRequest(DNSPacket *p, DNSPacket *r)
   bool haveOne=false;
   DNSSECPrivateKey dpk;
 
-  if(d_dk.haveActiveKSKFor(p->qdomain, &dpk)) {
-    rr.qtype=QType::DNSKEY;
-    rr.ttl=3600;
-    rr.qname=p->qdomain;
-    rr.content=dpk.getDNSKEY().getZoneRepresentation(); 
-    rr.auth = true;
-    r->addRecord(rr);
-    haveOne=true;
-  }
-
-  DNSSECKeeper::keyset_t zskset = d_dk.getKeys(p->qdomain, false);
-  BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
+  DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain);
+  BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
     rr.qtype=QType::DNSKEY;
     rr.ttl=3600;
     rr.qname=p->qdomain;
     rr.content=value.first.getDNSKEY().getZoneRepresentation();
-    
+    rr.auth=true;
     r->addRecord(rr);
     haveOne=true;
   }
@@ -935,7 +925,7 @@ DNSPacket *PacketHandler::question(DNSPacket *p)
 
 void PacketHandler::synthesiseRRSIGs(DNSPacket* p, DNSPacket* r)
 {
-  cerr<<"Need to fake up the RRSIGs some way.."<<endl;
+  cerr<<"Need to fake up the RRSIGs if someone asked for them explicitly"<<endl;
   B.lookup(QType(QType::ANY), p->qdomain, p);
   
   DNSResourceRecord rr;
@@ -996,13 +986,12 @@ void PacketHandler::synthesiseRRSIGs(DNSPacket* p, DNSPacket* r)
   rr.qtype = QType::RRSIG;
 
   BOOST_FOREACH(records_t::value_type& iter, records) {
-    RRSIGRecordContent rrc;
-    for(int ksk =0 ; ksk < 2; ++ksk) {
-      getRRSIGForRRSET(d_dk, p->qdomain, iter.first, 3600, iter.second, rrc, ksk);
+    vector<RRSIGRecordContent> rrcs;
+    
+    getRRSIGsForRRSET(d_dk, p->qdomain, iter.first, 3600, iter.second, rrcs, iter.first == QType::DNSKEY);
+    BOOST_FOREACH(RRSIGRecordContent& rrc, rrcs) {
       rr.content=rrc.getZoneRepresentation();
       r->addRecord(rr);
-      if(iter.first != QType::DNSKEY)
-        break;
     }
   }
 }
index 354dd35be89469af31de1933b845bcfd212056cf..0a1faf42b767ad9d4e362079bfed19dd75c2ada4 100644 (file)
@@ -174,6 +174,35 @@ void checkZone(DNSSECKeeper& dk, const std::string& zone)
   cerr<<"Checked "<<numrecords<<" records, "<<numerrors<<" errors"<<endl;
 }
 
+void showZone(DNSSECKeeper& dk, const std::string& zone)
+{
+  NSEC3PARAMRecordContent ns3pr;
+  bool narrow;
+  dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
+  
+  if(ns3pr.d_salt.empty()) 
+    cerr<<"Zone has NSEC semantics"<<endl;
+  else
+    cerr<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
+  
+  DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
+
+  if(keyset.empty())  {
+    cerr << "No keys for zone '"<<zone<<"'."<<endl;
+  }
+  else {  
+    cout << "keys: "<<endl;
+    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
+      cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
+      cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.d_key.getConstContext().len*8<<"\tActive: "<<value.second.active<< endl; // humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
+      if(value.second.keyOrZone) {
+        cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
+        cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << endl;
+        cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << endl << endl;
+      }
+    }
+  }
+}
 
 int main(int argc, char** argv)
 try
@@ -244,33 +273,7 @@ try
       return 0;
     }
     const string& zone=cmds[1];
-
-    NSEC3PARAMRecordContent ns3pr;
-    bool narrow;
-    dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
-    
-    if(ns3pr.d_salt.empty()) 
-      cerr<<"Zone has NSEC semantics"<<endl;
-    else
-      cerr<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
-    
-    DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
-
-    if(keyset.empty())  {
-      cerr << "No keys for zone '"<<zone<<"'."<<endl;
-    }
-    else {  
-      cout << "keys: "<<endl;
-      BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
-        cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
-        cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.d_key.getConstContext().len*8<<"\tActive: "<<value.second.active<< endl; // humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
-        if(value.second.keyOrZone) {
-          cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
-          cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << endl;
-          cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << endl << endl;
-        }
-      }
-    }
+    showZone(dk, zone);
   }
   else if(cmds[0] == "activate-zone-key") {
     const string& zone=cmds[1];
@@ -322,40 +325,29 @@ try
     const string& zone=cmds[1];
     DNSSECPrivateKey dpk;
     
-    if(dk.haveActiveKSKFor(zone, &dpk) && !g_vm.count("force")) {
-      cerr << "There is a key already for zone '"<<zone<<"', use --force to overwrite"<<endl;
+    if(dk.haveActiveKSKFor(zone)) {
+      cerr << "There is a KSK already for zone '"<<zone<<"', remove with pdnssec remove-zone-key if needed"<<endl;
       return 0;
     }
       
-    dk.secureZone(zone, 5);
+    dk.secureZone(zone, 8);
 
-    if(!dk.haveActiveKSKFor(zone, &dpk)) {
+    if(!dk.haveActiveKSKFor(zone)) {
       cerr << "This should not happen, still no key!" << endl;
       return 0;
     }
-    cout<<"Created KSK with tag "<<dpk.getDNSKEY().getTag()<<endl;
   
     DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
 
-    if(!zskset.empty() && !g_vm.count("force"))  {
-      cerr<<"There were ZSKs already for zone '"<<zone<<"'"<<endl;
+    if(!zskset.empty())  {
+      cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
       return 0;
     }
       
-    dk.addKey(zone, false, 5);
-    dk.addKey(zone, false, 5, 0, false); // not active
-
-    zskset = dk.getKeys(zone, false);
-    if(zskset.empty()) {
-      cerr<<"This should not happen, still no ZSK!"<<endl;
-    }
-
-    cout<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
-    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
-      cout<<"id = "<<value.second.id<<", tag = "<<value.first.getDNSKEY().getTag()<<", algo = "<<(int)value.first.d_algorithm<<
-        ", bits = "<<value.first.d_key.getContext().len*8;
-      cout<<"\tActive: "<<value.second.active<<endl;
-    }
+    dk.addKey(zone, false, 8);
+    dk.addKey(zone, false, 8, 0, false); // not active
+    rectifyZone(dk, zone);
+    showZone(dk, zone);
   }
   else if(cmds[0]=="set-nsec3") {
     string nsec3params =  cmds.size() > 2 ? cmds[2] : "1 0 1 ab";