diff --git a/TODO b/TODO
index 7ec13870..c063ffb0 100644
--- a/TODO
+++ b/TODO
@@ -29,9 +29,6 @@
   create qt4 qmake .prf for auto-discovery by applications
 
 * finish code for APIs:
-  redundant global static functions in publickey / cert
-  CertificateOptions: isValid
-  Certificate: operator==
   cert: rfc 2818 hostname validation
   tls
   sasl
diff --git a/include/QtCrypto/qca_cert.h b/include/QtCrypto/qca_cert.h
index c3fc29a2..889cf43b 100644
--- a/include/QtCrypto/qca_cert.h
+++ b/include/QtCrypto/qca_cert.h
@@ -175,6 +175,7 @@ namespace QCA
 		bool isSelfSigned() const;
 		int pathLimit() const;
 
+		QSecureArray signature() const;
 		SignatureAlgorithm signatureAlgorithm() const;
 
 		// import / export
@@ -222,6 +223,7 @@ namespace QCA
 		int pathLimit() const;               // PKCS#10 only
 		QString challenge() const;
 
+		QSecureArray signature() const;
 		SignatureAlgorithm signatureAlgorithm() const;
 
 		// import / export - PKCS#10 only
@@ -281,6 +283,7 @@ namespace QCA
 
 		QList<CRLEntry> revoked() const;
 
+		QSecureArray signature() const;
 		SignatureAlgorithm signatureAlgorithm() const;
 
 		// import / export
diff --git a/include/QtCrypto/qcaprovider.h b/include/QtCrypto/qcaprovider.h
index 5241f00c..d7003e8d 100644
--- a/include/QtCrypto/qcaprovider.h
+++ b/include/QtCrypto/qcaprovider.h
@@ -224,6 +224,7 @@ public:
 class CertContextProps
 {
 public:
+	int version;                     // cert only
 	QDateTime start, end;            // cert only
 	CertificateInfo subject;
 	CertificateInfo issuer;          // cert only
@@ -233,6 +234,7 @@ public:
 	bool isCA;
 	bool isSelfSigned;               // cert only
 	int pathLimit;
+	QSecureArray sig;
 	SignatureAlgorithm sigalgo;
 	QString challenge;               // csr only
 	CertificateRequestFormat format; // csr only
@@ -245,6 +247,7 @@ public:
 	int number;
 	QDateTime thisUpdate, nextUpdate;
 	QList<CRLEntry> revoked;
+	QSecureArray sig;
 	SignatureAlgorithm sigalgo;
 };
 
diff --git a/src/qca_cert.cpp b/src/qca_cert.cpp
index af6f77dc..80af6d72 100644
--- a/src/qca_cert.cpp
+++ b/src/qca_cert.cpp
@@ -29,43 +29,11 @@ namespace QCA {
 
 Provider::Context *getContext(const QString &type, const QString &provider);
 
-static bool stringToFile(const QString &fileName, const QString &content)
-{
-	QFile f(fileName);
-	if(!f.open(QFile::WriteOnly))
-		return false;
-	QTextStream ts(&f);
-	ts << content;
-	return true;
-}
-
-static bool stringFromFile(const QString &fileName, QString *s)
-{
-	QFile f(fileName);
-	if(!f.open(QFile::ReadOnly))
-		return false;
-	QTextStream ts(&f);
-	*s = ts.readAll();
-	return true;
-}
-
-static bool arrayToFile(const QString &fileName, const QByteArray &content)
-{
-	QFile f(fileName);
-	if(!f.open(QFile::WriteOnly))
-		return false;
-	f.write(content.data(), content.size());
-	return true;
-}
-
-static bool arrayFromFile(const QString &fileName, QByteArray *a)
-{
-	QFile f(fileName);
-	if(!f.open(QFile::ReadOnly))
-		return false;
-	*a = f.readAll();
-	return true;
-}
+// from qca_publickey.cpp
+bool stringToFile(const QString &fileName, const QString &content);
+bool stringFromFile(const QString &fileName, QString *s);
+bool arrayToFile(const QString &fileName, const QByteArray &content);
+bool arrayFromFile(const QString &fileName, QByteArray *a);
 
 //----------------------------------------------------------------------------
 // CertificateOptions
@@ -123,8 +91,14 @@ void CertificateOptions::setFormat(CertificateRequestFormat f)
 
 bool CertificateOptions::isValid() const
 {
-	// TODO: check the content
-	return false;
+	// logic from Botan
+	if(d->info.value(CommonName).isEmpty() || d->info.value(Country).isEmpty())
+		return false;
+	if(d->info.value(Country).length() != 2)
+		return false;
+	if(d->start >= d->end)
+		return false;
+	return true;
 }
 
 QString CertificateOptions::challenge() const
@@ -359,6 +333,11 @@ int Certificate::pathLimit() const
 	return static_cast<const CertContext *>(context())->props()->pathLimit;
 }
 
+QSecureArray Certificate::signature() const
+{
+	return static_cast<const CertContext *>(context())->props()->sig;
+}
+
 SignatureAlgorithm Certificate::signatureAlgorithm() const
 {
 	return static_cast<const CertContext *>(context())->props()->sigalgo;
@@ -428,10 +407,21 @@ bool Certificate::matchesHostname(const QString &realHost) const
 	return false;
 }
 
-bool Certificate::operator==(const Certificate &) const
+bool Certificate::operator==(const Certificate &cert) const
 {
-	// TODO
-	return false;
+	const CertContextProps *a = static_cast<const CertContext *>(context())->props();
+	const CertContextProps *b = static_cast<const CertContext *>(cert.context())->props();
+
+	// logic from Botan
+	if(a->sig != b->sig || a->sigalgo != b->sigalgo || subjectPublicKey() != cert.subjectPublicKey())
+		return false;
+	if(a->issuer != b->issuer || a->subject != b->subject)
+		return false;
+	if(a->serial != b->serial || a->version != b->version)
+		return false;
+	if(a->start != b->start || a->end != b->end)
+		return false;
+	return true;
 }
 
 bool Certificate::operator!=(const Certificate &a) const
@@ -535,6 +525,11 @@ QString CertificateRequest::challenge() const
 	return static_cast<const CSRContext *>(context())->props()->challenge;
 }
 
+QSecureArray CertificateRequest::signature() const
+{
+	return static_cast<const CSRContext *>(context())->props()->sig;
+}
+
 SignatureAlgorithm CertificateRequest::signatureAlgorithm() const
 {
 	return static_cast<const CSRContext *>(context())->props()->sigalgo;
@@ -675,6 +670,11 @@ QList<CRLEntry> CRL::revoked() const
 	return static_cast<const CRLContext *>(context())->props()->revoked;
 }
 
+QSecureArray CRL::signature() const
+{
+	return static_cast<const CRLContext *>(context())->props()->sig;
+}
+
 SignatureAlgorithm CRL::signatureAlgorithm() const
 {
 	return static_cast<const CRLContext *>(context())->props()->sigalgo;
diff --git a/src/qca_publickey.cpp b/src/qca_publickey.cpp
index 1c30910f..c13903d3 100644
--- a/src/qca_publickey.cpp
+++ b/src/qca_publickey.cpp
@@ -28,7 +28,7 @@ namespace QCA {
 
 Provider::Context *getContext(const QString &type, const QString &provider);
 
-static bool stringToFile(const QString &fileName, const QString &content)
+bool stringToFile(const QString &fileName, const QString &content)
 {
 	QFile f(fileName);
 	if(!f.open(QFile::WriteOnly))
@@ -38,13 +38,31 @@ static bool stringToFile(const QString &fileName, const QString &content)
 	return true;
 }
 
-static bool stringFromFile(const QString &fileName, QString *str)
+bool stringFromFile(const QString &fileName, QString *s)
 {
 	QFile f(fileName);
 	if(!f.open(QFile::ReadOnly))
 		return false;
 	QTextStream ts(&f);
-	*str = ts.readAll();
+	*s = ts.readAll();
+	return true;
+}
+
+bool arrayToFile(const QString &fileName, const QByteArray &content)
+{
+	QFile f(fileName);
+	if(!f.open(QFile::WriteOnly))
+		return false;
+	f.write(content.data(), content.size());
+	return true;
+}
+
+bool arrayFromFile(const QString &fileName, QByteArray *a)
+{
+	QFile f(fileName);
+	if(!f.open(QFile::ReadOnly))
+		return false;
+	*a = f.readAll();
 	return true;
 }