Viewing cetificate details

Damien Miller djm at mindrot.org
Thu Mar 4 13:08:58 EST 2010


On Wed, 3 Mar 2010, Iain Morgan wrote:

> Hi,
> 
> I don't see any way to view the details of a certificate once it is
> generated. Having such a capability would be very handy for debugging
> purposes to check what constraints, principals, and validity interval
> are associated with a given cert.

Yes, I had been meaning to add that - thanks for reminding me. This
diff adds "ssh-keygen -L" to print certificate details.

Index: ssh-keygen.1
===================================================================
RCS file: /cvs/src/usr.bin/ssh/ssh-keygen.1,v
retrieving revision 1.85
diff -u -p -r1.85 ssh-keygen.1
--- ssh-keygen.1	26 Feb 2010 22:09:28 -0000	1.85
+++ ssh-keygen.1	4 Mar 2010 02:07:48 -0000
@@ -115,6 +115,10 @@
 .Op Fl O Ar constraint
 .Op Fl V Ar validity_interval
 .Ar
+.Nm ssh-keygen
+.Bk -words
+.Fl L
+.Op Fl f Ar input_keyfile
 .Ek
 .Sh DESCRIPTION
 .Nm
@@ -275,6 +279,8 @@ also reads the
 RFC 4716 SSH Public Key File Format.
 This option allows importing keys from several commercial
 SSH implementations.
+.It Fl L
+Prints the contents of a certificate.
 .It Fl l
 Show fingerprint of specified public key file.
 Private RSA1 keys are also supported.
Index: ssh-keygen.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/ssh-keygen.c,v
retrieving revision 1.180
diff -u -p -r1.180 ssh-keygen.c
--- ssh-keygen.c	2 Mar 2010 23:20:57 -0000	1.180
+++ ssh-keygen.c	4 Mar 2010 02:07:49 -0000
@@ -74,6 +74,9 @@ int find_host = 0;
 /* Flag indicating that we want to delete a host from a known_hosts file */
 int delete_host = 0;
 
+/* Flag indicating that we want to show the contents of a certificate */
+int show_cert = 0;
+
 /* Flag indicating that we just want to see the key fingerprint */
 int print_fingerprint = 0;
 int print_bubblebabble = 0;
@@ -1055,7 +1058,7 @@ do_change_comment(struct passwd *pw)
 }
 
 static const char *
-fmt_validity(void)
+fmt_validity(u_int64_t valid_from, u_int64_t valid_to)
 {
 	char from[32], to[32];
 	static char ret[64];
@@ -1063,28 +1066,27 @@ fmt_validity(void)
 	struct tm *tm;
 
 	*from = *to = '\0';
-	if (cert_valid_from == 0 &&
-	    cert_valid_to == 0xffffffffffffffffULL)
+	if (valid_from == 0 && valid_to == 0xffffffffffffffffULL)
 		return "forever";
 
-	if (cert_valid_from != 0) {
+	if (valid_from != 0) {
 		/* XXX revisit INT_MAX in 2038 :) */
-		tt = cert_valid_from > INT_MAX ? INT_MAX : cert_valid_from;
+		tt = valid_from > INT_MAX ? INT_MAX : valid_from;
 		tm = localtime(&tt);
 		strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
 	}
-	if (cert_valid_to != 0xffffffffffffffffULL) {
+	if (valid_to != 0xffffffffffffffffULL) {
 		/* XXX revisit INT_MAX in 2038 :) */
-		tt = cert_valid_to > INT_MAX ? INT_MAX : cert_valid_to;
+		tt = valid_to > INT_MAX ? INT_MAX : valid_to;
 		tm = localtime(&tt);
 		strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
 	}
 
-	if (cert_valid_from == 0) {
+	if (valid_from == 0) {
 		snprintf(ret, sizeof(ret), "before %s", to);
 		return ret;
 	}
-	if (cert_valid_to == 0xffffffffffffffffULL) {
+	if (valid_to == 0xffffffffffffffffULL) {
 		snprintf(ret, sizeof(ret), "after %s", from);
 		return ret;
 	}
@@ -1208,7 +1210,7 @@ do_ca_sign(struct passwd *pw, int argc, 
 			    out, cert_key_id,
 			    cert_principals != NULL ? " for " : "",
 			    cert_principals != NULL ? cert_principals : "",
-			    fmt_validity());
+			    fmt_validity(cert_valid_from, cert_valid_to));
 
 		key_free(public);
 		xfree(out);
@@ -1358,6 +1360,89 @@ add_cert_constraint(char *opt)
 }
 
 static void
+do_show_cert(struct passwd *pw)
+{
+	Key *key;
+	struct stat st;
+	char *key_fp, *ca_fp;
+	Buffer constraints, constraint;
+	u_char *name, *data;
+	u_int i, dlen;
+
+	if (!have_identity)
+		ask_filename(pw, "Enter file in which the key is");
+	if (stat(identity_file, &st) < 0) {
+		perror(identity_file);
+		exit(1);
+	}
+	if ((key = key_load_public(identity_file, NULL)) == NULL)
+		fatal("%s is not a public key", identity_file);
+	if (!key_is_cert(key))
+		fatal("%s is not a certificate", identity_file);
+	
+	key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+	ca_fp = key_fingerprint(key->cert->signature_key,
+	    SSH_FP_MD5, SSH_FP_HEX);
+
+	printf("%s:\n", identity_file);
+	printf("        %s certificate %s\n", key_type(key), key_fp);
+	printf("        Signed by %s CA %s\n",
+	    key_type(key->cert->signature_key), ca_fp);
+	printf("        Key ID \"%s\"\n", key->cert->key_id);
+	printf("        Valid: %s\n",
+	    fmt_validity(key->cert->valid_after, key->cert->valid_before));
+	printf("        Principals: ");
+	if (key->cert->nprincipals == 0)
+		printf("(none)\n");
+	else {
+		for (i = 0; i < key->cert->nprincipals; i++)
+			printf("\n                %s",
+			    key->cert->principals[i]);
+		printf("\n");
+	}
+	printf("        Constraints: ");
+	if (buffer_len(&key->cert->constraints) == 0)
+		printf("(none)\n");
+	else {
+		printf("\n");
+		buffer_init(&constraints);
+		buffer_append(&constraints,
+		    buffer_ptr(&key->cert->constraints),
+		    buffer_len(&key->cert->constraints));
+		buffer_init(&constraint);
+		while (buffer_len(&constraints) != 0) {
+			name = buffer_get_string(&constraints, NULL);
+			data = buffer_get_string_ptr(&constraints, &dlen);
+			buffer_append(&constraint, data, dlen);
+			printf("                %s", name);
+			if (strcmp(name, "permit-X11-forwarding") == 0 ||
+			    strcmp(name, "permit-agent-forwarding") == 0 ||
+			    strcmp(name, "permit-port-forwarding") == 0 ||
+			    strcmp(name, "permit-pty") == 0 ||
+			    strcmp(name, "permit-user-rc") == 0)
+				printf("\n");
+			else if (strcmp(name, "force-command") == 0 ||
+			    strcmp(name, "source-address") == 0) {
+				data = buffer_get_string(&constraint, NULL);
+				printf(" %s\n", data);
+				xfree(data);
+			} else {
+				printf(" UNKNOWN CONSTRAINT (len %u)\n",
+				    buffer_len(&constraint));
+				buffer_clear(&constraint);
+			}
+			xfree(name);
+			if (buffer_len(&constraint) != 0)
+				fatal("Constraint corrupt: extra data at end");
+		}
+		buffer_free(&constraint);
+		buffer_free(&constraints);
+	}
+
+	exit(0);
+}
+
+static void
 usage(void)
 {
 	fprintf(stderr, "usage: %s [options]\n", __progname);
@@ -1379,6 +1464,7 @@ usage(void)
 	fprintf(stderr, "  -h          Generate host certificate instead of a user certificate.\n");
 	fprintf(stderr, "  -I key_id   Key identifier to include in certificate.\n");
 	fprintf(stderr, "  -i          Convert RFC 4716 to OpenSSH key file.\n");
+	fprintf(stderr, "  -L          Print the contents of a certificate.\n");
 	fprintf(stderr, "  -l          Show fingerprint of key file.\n");
 	fprintf(stderr, "  -M memory   Amount of memory (MB) to use for generating DH-GEX moduli.\n");
 	fprintf(stderr, "  -n name,... User/host principal names to include in certificate\n");
@@ -1440,7 +1526,7 @@ main(int argc, char **argv)
 		exit(1);
 	}
 
-	while ((opt = getopt(argc, argv, "degiqpclBHhvxXyF:b:f:t:D:I:P:N:n:"
+	while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:N:n:"
 	    "O:C:r:g:R:T:G:M:S:s:a:V:W:")) != -1) {
 		switch (opt) {
 		case 'b':
@@ -1463,6 +1549,9 @@ main(int argc, char **argv)
 			delete_host = 1;
 			rr_hostname = optarg;
 			break;
+		case 'L':
+			show_cert = 1;
+			break;
 		case 'l':
 			print_fingerprint = 1;
 			break;
@@ -1616,6 +1705,8 @@ main(int argc, char **argv)
 			fatal("Must specify key id (-I) when certifying");
 		do_ca_sign(pw, argc, argv);
 	}
+	if (show_cert)
+		do_show_cert(pw);
 	if (delete_host || hash_hosts || find_host)
 		do_known_hosts(pw, rr_hostname);
 	if (print_fingerprint || print_bubblebabble)


More information about the openssh-unix-dev mailing list