diff --git a/PyKAdminPrincipalObject.c b/PyKAdminPrincipalObject.c
index 22a565519b6ad86000aab54d5595f2a1db275912..c51e923601663f1cd73dae80c815996c1d1b6dd5 100644
--- a/PyKAdminPrincipalObject.c
+++ b/PyKAdminPrincipalObject.c
@@ -12,6 +12,9 @@
 
 #define TIME_NONE ((time_t) -1)
 
+
+static PyObject *PyKAdminPrincipal_get_keys(PyKAdminPrincipalObject *self, void *closure);
+
 static char kNEVER[] = "never";
 
 static const unsigned int kFLAG_MAX =
@@ -68,12 +71,55 @@ static PyObject *PyKAdminPrincipal_str(PyKAdminPrincipalObject *self) {
 }
 
 
+static void _PyKAdminPrincipal_print_keys(PyKAdminPrincipalObject *self, FILE *file, int flags) {
+
+    PyObject *keys = PyKAdminPrincipal_get_keys(self, NULL);
+
+    PyObject *kvno;
+    PyObject *value;
+    PyObject *tuple;
+    PyObject *enctype;
+    PyObject *salttype;
+
+    ssize_t pos = 0;
+    ssize_t index = 0; 
+
+    if (keys) {
+
+        while (PyDict_Next(keys, &pos, &kvno, &value)) {
+
+            if (PyList_Check(value)) {
+
+                for (index = 0; index < PyList_Size(value); index++) {
+
+                    tuple = PyList_GetItem(value, index);
+
+                    if (PyTuple_Check(tuple)) {
+
+                        if (PyTuple_Size(tuple) == 2) {
+
+                            enctype = PyTuple_GetItem(tuple, 0);
+                            salttype = PyTuple_GetItem(tuple, 1);
+
+                            fprintf(file, "Key: vno %ld, %s, %s\n", PyUnifiedLongInt_AsLong(kvno), PyUnicode_or_PyBytes_asCString(enctype), PyUnicode_or_PyBytes_asCString(salttype));
+
+                        }
+                    }
+                }
+            }
+        }
+
+        Py_DECREF(keys);
+    }
+
+}
+
 static int PyKAdminPrincipal_print(PyKAdminPrincipalObject *self, FILE *file, int flags){
 
     static char kNEVER_DATE[] = "[never]";
     static char kNONE_DATE[]  = "[none]";
 
-    static const char *kPRINT_FORMAT = "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %d\n%s: %s\n%s: %s";
+    //static const char *kPRINT_FORMAT = "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %d\n%s: %d\n%s: %s";
 
     krb5_error_code errno;
     char *client_name = NULL;
@@ -100,20 +146,23 @@ static int PyKAdminPrincipal_print(PyKAdminPrincipalObject *self, FILE *file, in
         maxlife  = pykadmin_timestamp_as_deltastr(self->entry.max_life, kNONE_DATE);
         maxrlife = pykadmin_timestamp_as_deltastr(self->entry.max_renewable_life, kNONE_DATE);
 
-        fprintf(file, kPRINT_FORMAT, 
-            "Principal",                      client_name,
-            "Expiration date",                expire,
-            "Last password change",           pwchange,
-            "Password expiration date",       pwexpire,
-            "Maximum ticket life",            maxlife,
-            "Maximum renewable life",         maxrlife,
-            "Last modified",                  moddate,
-            "Last successful authentication", success,
-            "Last failed authentication",     failure,
-            "Failed password attempts",       self->entry.fail_auth_count,
-            "Number of keys",                 "(TODO)",
-            "Policy",                         self->entry.policy ? self->entry.policy : kNONE_DATE
-            );
+        //fprintf(file, kPRINT_FORMAT, 
+        fprintf(file, "Principal: %s\n",                      client_name);
+        fprintf(file, "Expiration date: %s\n",                expire);
+        fprintf(file, "Last password change: %s\n",           pwchange);
+        fprintf(file, "Password expiration date: %s\n",       pwexpire);
+        fprintf(file, "Maximum ticket life: %s\n",            maxlife);
+        fprintf(file, "Maximum renewable life: %s\n",         maxrlife);
+        fprintf(file, "Last modified: %s\n",                  moddate);
+        fprintf(file, "Last successful authentication: %s\n", success);
+        fprintf(file, "Last failed authentication: %s\n",     failure);
+        fprintf(file, "Failed password attempts: %d\n",       self->entry.fail_auth_count);
+        fprintf(file, "Number of keys: %d\n",                 self->entry.n_key_data);
+
+        _PyKAdminPrincipal_print_keys(self, file, flags);
+
+        fprintf(file, "Policy: %s", self->entry.policy ? self->entry.policy : kNONE_DATE );
+
     }
 
     if (client_name) { free(client_name); }
@@ -409,6 +458,89 @@ static PyObject *PyKAdminPrincipal_get_kvno(PyKAdminPrincipalObject *self, void
 }
 
 
+
+PyObject *pykadmin_key_enctype_name(krb5_key_data *key_data) {
+
+    PyObject *enctype = NULL;
+    // make sure this is enough. 
+    char buffer[1024];
+
+    if (krb5_enctype_to_name(key_data->key_data_type[0], FALSE, buffer, sizeof(buffer)))
+        snprintf(buffer, sizeof(buffer), "<Encryption type 0x%x>", key_data->key_data_type[0]);
+
+    enctype = PyUnicode_FromString(buffer);
+
+    return enctype;
+}
+
+PyObject *pykadmin_key_salttype_name(krb5_key_data *key_data) {
+
+    PyObject *salttype = NULL;
+    // make sure this is enough. 
+    char buffer[1024];
+
+    if (krb5_salttype_to_string(key_data->key_data_type[1], buffer, sizeof(buffer)))
+        snprintf(buffer, sizeof(buffer), "<Salt type 0x%x>", key_data->key_data_type[0]);
+
+    salttype = PyUnicode_FromString(buffer);
+
+    return salttype;
+}
+
+static PyObject *PyKAdminPrincipal_get_keys(PyKAdminPrincipalObject *self, void *closure) { 
+
+    /*
+
+    key structure:
+
+        {
+            kvno: [("enctype", "salt"), ("enctype", "salt")],
+            kvno: ...
+        }
+
+    */
+
+    PyObject *kvno     = NULL;
+    PyObject *enctype  = NULL;
+    PyObject *salttype = NULL;
+    PyObject *tuple    = NULL;
+    PyObject *list     = NULL;
+
+    PyObject *keys = PyDict_New();
+
+    ssize_t index = 0; 
+
+    for (; index < self->entry.n_key_data; index++) {
+
+        krb5_key_data *key_data = &self->entry.key_data[index];
+
+        kvno = PyUnifiedLongInt_FromLong(key_data->key_data_kvno);
+
+        enctype  = pykadmin_key_enctype_name(key_data);
+        salttype = pykadmin_key_salttype_name(key_data);
+
+        tuple = PyTuple_Pack(2, enctype, salttype);
+
+
+        if (kvno) {
+            if (PyDict_Contains(keys, kvno)) {
+                list = PyDict_GetItem(keys, kvno);
+            } else {
+                list = PyList_New(0);
+                PyDict_SetItem(keys, kvno, list);
+            }
+        }
+
+        if (list && tuple) {
+            PyList_Append(list, tuple);
+        }
+
+    }
+
+    return keys;
+}
+
+
 /*
  *  SETTERS 
  */
@@ -747,6 +879,7 @@ static PyGetSetDef PyKAdminPrincipal_getters_setters[] = {
 
     {"attributes",      (getter)PyKAdminPrincipal_get_attributes,      NULL, kDOCSTRING_ATTRIBUTES,      NULL},
 
+    {"keys",            (getter)PyKAdminPrincipal_get_keys,            NULL, "",                         NULL},
 
     // setter attributes
 
@@ -833,7 +966,7 @@ PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_name(PyKAdminObj
             principal->kadmin = kadmin;
 
             errno = krb5_parse_name(kadmin->context, client_name, &temp);
-            retval = kadm5_get_principal(kadmin->server_handle, temp, &principal->entry, KADM5_PRINCIPAL_NORMAL_MASK);
+            retval = kadm5_get_principal(kadmin->server_handle, temp, &principal->entry, (KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA));
 
             krb5_free_principal(kadmin->context, temp);
 
@@ -860,7 +993,7 @@ PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_db_entry(PyKAdmi
         Py_INCREF(kadmin);
         principal->kadmin = kadmin;
 
-        retval = pykadmin_kadm_from_kdb(kadmin, kdb, &principal->entry, KADM5_PRINCIPAL_NORMAL_MASK);
+        retval = pykadmin_kadm_from_kdb(kadmin, kdb, &principal->entry, (KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA));
 
         if (retval) {
             PyKAdminPrincipal_dealloc(principal);
diff --git a/pykadmin.h b/pykadmin.h
index 6d51cf5f8ce83de2b09d9f7b9fef2c1dae839302..61908a034615130a5f0e6250b76d10aaf4ae1b65 100644
--- a/pykadmin.h
+++ b/pykadmin.h
@@ -20,10 +20,12 @@ struct module_state {
 #	define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
 # 	define PyUnifiedLongInt_FromLong(from) PyLong_FromLong((long) from)
 #	define PyUnifiedLongInt_AsUnsignedLong(ob) PyLong_AsUnsignedLong((PyObject *)ob)
+#	define PyUnifiedLongInt_AsLong(ob) PyLong_AsLong((PyObject *)ob)
 #else 
 #   define GETSTATE(m) (&_state)    
 # 	define PyUnifiedLongInt_FromLong(from) PyInt_FromLong((long) from)
 #	define PyUnifiedLongInt_AsUnsignedLong(ob) PyInt_AsUnsignedLongMask((PyObject *)ob)
+#	define PyUnifiedLongInt_AsLong(ob) PyInt_AsLong((PyObject *)ob)
 #endif
 
 #ifndef Py_TYPE