Self-verifying names

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Self-verifying names

Sean Rhea
All,

I'm trying to use OpenSSL in a rather peculiar way, and I wonder if  
anyone here can help me out.  I have two peers.  Each has an RSA  
public-private key pair and a self-signed X509 certificate.  What I'd  
like to do is establish a TLS connection between the two of them  
where each can learn the public key fingerprint of the party on the  
other side, and also verify that the party on the other side knows  
the corresponding private key.  I don't want to accept any other kind  
of connections.  In particular, any request from a party with no  
certificate or a non-self-signed certificate should be rejected.

The idea here is that I'm using a peer's public key as its identity.  
I don't care what it's "real" name is, or about any of the rest of  
the information in the X509 certificate for that matter.  All I care  
is that it knows the private key corresponding to the public key that  
it's using as its identity.

 From the code I've played with so far, it seems that I'll need to  
call SSL_CTX_set_verify with a callback that catches the error  
OpenSSL generates when it finds a self-signed, non-root certificate,  
and do my check in there.  But I can't figure out what functions to  
use to check the signature on the self-signed cert, or how to confirm  
that the public key in that cert is the same as the one the peer is  
trying to use to authenticate the connection.

Am I making any sense?  Any ideas?

Thanks,
Sean
--
      Other people can talk about how to expand the destiny of mankind.
      I just want to talk about how to fix a motorcycle.  I think that
                 what I have to say has more lasting value.
       -- Robert M. Pirsig, Zen and the Art of Motorcycle Maintenance




PGP.sig (193 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Victor Duchovni
On Wed, Nov 30, 2005 at 04:18:44PM -0500, Sean Rhea wrote:

> All,
>
> I'm trying to use OpenSSL in a rather peculiar way, and I wonder if  
> anyone here can help me out.  I have two peers.  Each has an RSA  
> public-private key pair and a self-signed X509 certificate.  What I'd  
> like to do is establish a TLS connection between the two of them  
> where each can learn the public key fingerprint of the party on the  
> other side, and also verify that the party on the other side knows  
> the corresponding private key.  I don't want to accept any other kind  
> of connections.  In particular, any request from a party with no  
> certificate or a non-self-signed certificate should be rejected.
>
> The idea here is that I'm using a peer's public key as its identity.  
> I don't care what it's "real" name is, or about any of the rest of  
> the information in the X509 certificate for that matter.  All I care  
> is that it knows the private key corresponding to the public key that  
> it's using as its identity.
>
> From the code I've played with so far, it seems that I'll need to  
> call SSL_CTX_set_verify with a callback that catches the error  
> OpenSSL generates when it finds a self-signed, non-root certificate,  
> and do my check in there.  But I can't figure out what functions to  
> use to check the signature on the self-signed cert, or how to confirm  
> that the public key in that cert is the same as the one the peer is  
> trying to use to authenticate the connection.
>
> Am I making any sense?  Any ideas?
>

This is completely doable. Example code to be found in many SSL
applications.

        http://www.postfix.org/TLS_README.html#server_vrfy_client
        http://www.postfix.org/TLS_README.html#server_access
        http://www.postfix.org/postconf.5.html#permit_tls_clientcerts

Source code:

        http://www.postfix.org/dowload.html

get 2.3-20051128 and look at:

        src/tls/tls_verify.c
        src/tls/tls_server.c
        src/tls/tls_client.c

--
        Viktor.
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Sean Rhea
On Nov 30, 2005, at 4:42 PM, Victor Duchovni wrote:

> This is completely doable. Example code to be found in many SSL
> applications.
>
> http://www.postfix.org/TLS_README.html#server_vrfy_client
> http://www.postfix.org/TLS_README.html#server_access
> http://www.postfix.org/postconf.5.html#permit_tls_clientcerts
>
> Source code:
>
> http://www.postfix.org/dowload.html
>
> get 2.3-20051128 and look at:
>
> src/tls/tls_verify.c
> src/tls/tls_server.c
> src/tls/tls_client.c
Sorry, I don't actually see that the postfix code is doing what I  
want.  I see that you're computing the client's fingerprint in  
tls_server.c, but that's only after SSL has verified the client  
certificate.  In my case, the client has a self-signed certificate,  
so the verification fails.  As a result (I think),  
SSL_get_peer_certificate is returning NULL (see sample code below).

Sean
--
                    Boredom is always counterrevolutionary.


// Simple echo server using TLS

int
main(int argc, char *argv[])
{
     SSL_load_error_strings();
     SSL_library_init();

     SSL_CTX *ctx = SSL_CTX_new(TLSv1_server_method());
     assert(ctx);

     SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |  
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                        NULL);

     if (!SSL_CTX_use_certificate_file(ctx, "server-cert.pem",  
SSL_FILETYPE_PEM))
         error("server: load cert error");

     if (!SSL_CTX_use_PrivateKey_file(ctx, "server-priv.pem",  
SSL_FILETYPE_PEM))
         error("server: load key error");

     if (!SSL_CTX_check_private_key(ctx))
         error("server: private key doesn't match cert");

     SSL *ssl = SSL_new(ctx);
     assert(ssl);

     int ssock = socket(AF_INET, SOCK_STREAM, 0);

     int n = 1;
     setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

     struct sockaddr_in addr;
     addr.sin_family = AF_INET;
     addr.sin_port = htons(2000);
     addr.sin_addr.s_addr = INADDR_ANY;

     if (bind(ssock, (const sockaddr*) &addr, sizeof(addr))) {
         perror("bind:");
         assert(0);
     }

     if (listen(ssock, 5))
         assert(0);

     while (1) {
         printf("server: waiting for accept\n");
         socklen_t addrlen = sizeof(addr);
         int sock = accept(ssock, (sockaddr*) &addr, &addrlen);
         if (sock <= 0)
             assert(0);

         printf("server: tcp accept succeeded\n");

         if (!SSL_set_fd(ssl, sock))
             assert(0);

         int ret;
         if ((ret = SSL_accept(ssl)) == 0) {
             int err = SSL_get_error(ssl, ret);
             printf("server: accept err=%d\n", err);
             assert(0);
         }

         printf("server: ssl accept succeeded\n");

         // XXX: this fails:
         assert(SSL_get_verify_result(ssl) == X509_V_OK);

         X509 *peer = SSL_get_peer_certificate(ssl);

         // XXX: and if you comment out the above assert, this fails,  
too:
         assert(peer);

         int n;
         char readbuf[256];
         while ((n = SSL_read(ssl, readbuf, sizeof(readbuf))) > 0) {
             printf("server: read %d bytes\n", n);
             if (SSL_write(ssl, readbuf, n) != n)
                 break;
             printf("server: wrote %d bytes\n", n);
         }

         printf("server: connection closed\n");
     }

     return 0;
}


PGP.sig (193 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Victor Duchovni
On Wed, Nov 30, 2005 at 06:07:20PM -0500, Sean Rhea wrote:

> On Nov 30, 2005, at 4:42 PM, Victor Duchovni wrote:
> >This is completely doable. Example code to be found in many SSL
> >applications.
> >
> > http://www.postfix.org/TLS_README.html#server_vrfy_client
> > http://www.postfix.org/TLS_README.html#server_access
> > http://www.postfix.org/postconf.5.html#permit_tls_clientcerts
> >
> >Source code:
> >
> > http://www.postfix.org/dowload.html
> >
> >get 2.3-20051128 and look at:
> >
> > src/tls/tls_verify.c
> > src/tls/tls_server.c
> > src/tls/tls_client.c
>
> Sorry, I don't actually see that the postfix code is doing what I  
> want.  I see that you're computing the client's fingerprint in  
> tls_server.c, but that's only after SSL has verified the client  
> certificate.  In my case, the client has a self-signed certificate,  
> so the verification fails.  As a result (I think),  
> SSL_get_peer_certificate is returning NULL (see sample code below).
>

Yes, but the verification is optional just tell SSL that the certs
verify OK. Postfix only uses fingerprints of verified clients, but you
don't have to do that. The machinery is much the same. In the Postfix client,
the server verification is optional.

--
        Viktor.
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Sean Rhea
On Nov 30, 2005, at 8:24 PM, Victor Duchovni wrote:
> Yes, but the verification is optional just tell SSL that the certs
> verify OK. Postfix only uses fingerprints of verified clients, but you
> don't have to do that. The machinery is much the same. In the  
> Postfix client,
> the server verification is optional.

Okay, but I need to do _some_ verification:

   - I need to check that the remote peer has provided a self-signed  
cert.
     Can I do that by checking that like this?

     static int
     verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
     {
       if (X509_STORE_CTX_get_error(ctx)
           != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
         fail();

   - I need to check that that cert is in fact self-signed.  Can I do it
     like this?

        X509 *err_cert = X509_STORE_CTX_get_current_cert(ctx);
        if (!X509_verify(err_cert, X509_PUBKEY_get(err_cert-
 >cert_info->key)))
          failure();

   - I need to get the public key with which it was signed,  
presumably like
     this: X509_PUBKEY_get(err_cert->cert_info->key))

And how do I get SSL_get_peer_certificate() to return non-NULL later  
on?  There doesn't seem to be a corresponding set function.

Thanks a bunch for the quick responses!
Sean
--
         Human beings were created by water to transport it uphill.
                               -- Unix fortune




PGP.sig (193 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Victor Duchovni
On Wed, Nov 30, 2005 at 09:52:57PM -0500, Sean Rhea wrote:

> Okay, but I need to do _some_ verification:

No, not really. Clearly OpenSSL has already verified that the client
has a private key that matches the public key in the certificate, or
else all the certificace checks are pointless. At this point, you can
(ideally) just use/authorize its public key alone and completely ignore
the contents of all the ceritificates, in a bilateral trust relationship
the certificates add no value.

>   - I need to get the public key with which it was signed,  
> presumably like
>     this: X509_PUBKEY_get(err_cert->cert_info->key))

> And how do I get SSL_get_peer_certificate() to return non-NULL later  
> on?  There doesn't seem to be a corresponding set function.

Why should it return NULL? If the client supplied a certificate,
and your verification callback in the server said it was OK (just
say yes), once the session is established check the public key.

The Postfix TLS client, for example, delays peer verification until
after the SSL handshake completes, the verify callback just notes
whether the certificates are valid (in your case they always are,
because you don't care), but the CN and SubjectAltNames (in your
cases the public key) are checked later.

Likewise in the Postfix server, the client certificate validity (
again you don't care) is checked early, but the cert fingerprint
(ideally you would directly use the public key) is checked later.

Now as far as the public key, this is more subtle, because x.509 supports
a variety of algorithms, and the underlying type specific data is not
very easy to get at. So, using the certificate fingeprint as a proxy
for the public key is likely simplest in practice.

Does anyone else have robust code for pulling public keys out of
peer x.509 objects?

--
        Viktor.
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Sean Rhea
On Nov 30, 2005, at 10:48 PM, Victor Duchovni wrote:
> Clearly OpenSSL has already verified that the client
> has a private key that matches the public key in the certificate, or
> else all the certificace checks are pointless.

Are you SURE about that?  I'm not doubting you, it's just that  
"Clearly..." sounds more like an assumption.

> Why should it return NULL? If the client supplied a certificate,
> and your verification callback in the server said it was OK (just
> say yes), once the session is established check the public key.

Okay, I think I have that working.  Three sample files are attached.  
The client and server sides, and a common verification routine.  
Would you mind looking them over and letting me know whether they're  
doing what I want?  I THINK they are, but security-sensitive code can  
always use a second set of eyeballs.  :)


The output, by the way, looks like this:

$ ./server
server: waiting for accept
server: tcp accept succeeded
server: ssl accept succeeded
client cert digest=c052b579972919c474e99604443ca0b6d50b215d
server: read 14 bytes
server: wrote 14 bytes
server: connection closed

$ ./client
client: tcp connect succeeded
client: ssl connect succeeded
server cert digest=3426940b3247899a536046b58d71a9f2900e63f9
Hello, World!

$ openssl x509 -fingerprint -in client-cert.pem -sha1 -nooutSHA1  
Fingerprint=C0:52:B5:79:97:29:19:C4:74:E9:96:04:44:3C:A0:B6:D5:0B:21:5D
$ openssl x509 -fingerprint -in server-cert.pem -sha1 -nooutSHA1  
Fingerprint=34:26:94:0B:32:47:89:9A:53:60:46:B5:8D:71:A9:F2:90:0E:63:F9


Thanks again for all your help,
Sean
--
            Humanity has advanced, when it has advanced, not because
            it has been sober, responsible, and cautious, but because
                  it has been playful, rebellious, and immature.
                                  -- Tom Robbins





client.C (2K) Download Attachment
server.C (2K) Download Attachment
common.C (1K) Download Attachment
PGP.sig (193 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Victor Duchovni
On Thu, Dec 01, 2005 at 04:08:38PM -0500, Sean Rhea wrote:

> >Clearly OpenSSL has already verified that the client
> >has a private key that matches the public key in the certificate, or
> >else all the certificace checks are pointless.
>
> Are you SURE about that?  I'm not doubting you, it's just that  
> "Clearly..." sounds more like an assumption.

Well, it cannot be otherwise, because if that is not the case, there is
nothing your verification code can do to check this, because you don't
get to participate in the protocol handshake and verify the client's
signature on some nonce using the public key from the certificate (the
key exchange happens outside your callback).

All you get to verify is the trust chain, but it is completely irrelevant,
you already know who the client's expected public key or the fingerprint
of his self-signed certificate.

> Okay, I think I have that working.  Three sample files are attached.  
> The client and server sides, and a common verification routine.  
> Would you mind looking them over and letting me know whether they're  
> doing what I want?  I THINK they are, but security-sensitive code can  
> always use a second set of eyeballs.  :)
>

Looks ok to me, but I am hoping that someone else will take a look at it
also. In your verification callback the final call to X509_verify is
pointless:

    int
    verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
    {
        // Should have failed due to it being self-signed.

        int err = X509_STORE_CTX_get_error(ctx);
        if (err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
            return 0;

        // Double check that the certificate is properly signed.
        // XXX: is that what X509_verify actually does???

        X509 *err_cert = X509_STORE_CTX_get_current_cert(ctx);
        return X509_verify(err_cert, X509_PUBKEY_get(err_cert->cert_info->key));
    }

It does not matter who signed the certificate. Indeed the callback can
always return 1 even if the certificate is not self-signed. The properties
you want are:

    - The peer has proved possesion of the private key that matches
      the certificate public key (the SSL handshake does that).

    - The public key (via a fingerprint for convenience) is the one
      that you want.

So I would change this to:

    int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
    {
        return 1;
    }

You can test this. Give the client or server a the wrong private key
and the right certificate and watch the SSL handshake fail, even though
you ignore the validity of the certificate chain.

Anyone else have comments on the above?

--
        Viktor.
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: Self-verifying names

Sean Rhea
On Dec 2, 2005, at 2:17 AM, Victor Duchovni wrote:

> So I would change this to:
>
>     int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
>     {
> return 1;
>     }
>
> You can test this. Give the client or server a the wrong private key
> and the right certificate and watch the SSL handshake fail, even  
> though
> you ignore the validity of the certificate chain.
Okay, will do.

Thanks again for all your help!
Sean
--
     Once, someone asked me what pleasure I took in riding for so long.
       'Pleasure?' I said. 'I don't understand the question.' I didn't
                   do it for pleasure. I did it for pain.
                             -- Lance Armstrong




PGP.sig (193 bytes) Download Attachment