Imagine you have a secure side channel for communicating a few bytes of verification information (like a phone, for example). I'd like to make it so I could connect two systems without exchanging keys in advance (and without the man-in-the-middle problems of Diffie-Helman), with the important consideration that I know I'm not smart enough to design my own cryptosystem and it's best to stay within the bounds of existing protocols.
With that in mind it seems natural to use is self-signed x509 certificates, the bread and butter of TLS, and have the endpoints use their side channel to verify that the certificate fingerprints (a small amount of data) are as they expect.
But it also seems that when you tell gnutls's server to require a client certificate, it does so in the form of sending a certificate request to the client that includes a list of all the certificate authorities the server trusts. Since there are no authorities here, it sends an empty list, and the client gives up. The gnutls docs refer to a function that controls whether the server sends such a list, but that function (while documented) is not actually exposed by the gnutls API. And even if it were, I wonder if there's a reason nobody is using it -- maybe it doesn't solve my problem.
Another idea is to include a built-in CA key/cert with every client, which can then be used to sign any arbitrary cert. (It's sorta like the current state of the art of commercial CAs! :P) It would then hopefully be successfully exchanged by the above process, just leaving me to be very careful to only trust the client cert if it's from a good fingerprint.
But this gets me dangerously close to the "design my own cryptosystem", and I can't help but think people have already solved these sorts of problems. So my question for you: what's the right way to do this?