Why is PKI so hard? – Page 4 – Tales from the Crypto

Why is PKI so hard?

How to scan SSL/TLS sites.

The other day, I hit a conundrum.

We couldn’t make LDAPS connections to a couple of domain controllers. A quick “TS” over to the systems in question indicated that we had a correct certificate in place, and that it was valid, but when we connected using “LDP” over port 636, we would be told that the certificate exchange wasn’t allowed to finish.

A look at the event viewer showed us that the certificate had expired. That’s ludicrous, though because the certificates show in the certificate manager as valid.

The only thing we could figure out is that we must have been running into the problem specified in KB 321051 – “How to enable LDAP over SSL…” – or at least, the last issue on “Pre-SP3 SSL certificate caching issue”. Sure, we’re on SP4, but that’s the only thing we could figure out.

To test it further, we had to view the certificate, and the brutal method we chose was to open up Internet Explorer and open https://192.168.0.1:636 – where “192.168.0.1” was the address of the DC whose certificate we wanted to view.

An error comes up on screen when you do this, because the IP address doesn’t match the name. You want this error, because it allows you to press “View Certificate”.

Sure enough, the certificate that came up was old, and expired. And, just as the article assured us we shouldn’t have to do on a Windows 2000 SP4 box, a re-boot fixed the server to using the right certificate.

If you’re thinking like we were, your next though will be “gee, I wonder how many of our other servers have this problem?” Obviously, we would be hard-pressed to scan all of our servers using an Internet Explorer error dialog, so it was time to write a program to do it for us.

Less than an hour later, I had the program I like to call “SSLScan”.

Sure, it’s in C# .NET 2.0, but it’s probably the shortest piece of SSL code I’ve written that wasn’t completely densely obfuscated.

using System;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Collections;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace SSLScan
{
class Program
{
static int port = 443; // We choose to default to HTTPS.
// Other examples – LDAPS is 636.

static void Main(string[] args)
{
//
// SSLScan – scan a list of SSL servers, connecting to each
// in turn, and asking for their certificate.
// Then display certificate information.
//
// We assume that each server responds to an SSL ClientHello
// immediately upon connection – this will not work with some
// SSL servers, for instance FTPS servers, that require some
// sort of command(s) to be issued prior to sending the SSL
// negotiation.
//
// For now, the only argument is the name of the file used to list
// the hosts – or you can feed it as stdin.
//
switch (args.Length)
{
case 0:
Usage();
Console.Error.WriteLine(“Reading from keyboard”);
TestConnectionsFromStream(new System.IO.StreamReader(
System.Console.OpenStandardInput()));
break;

case 1:
Console.Error.WriteLine(“Reading from file {0}”, args[0]);
TestConnectionsFromStream(new System.IO.StreamReader(args[0]));
break;

default:
Usage();
break;
}
}

private static void TestConnectionsFromStream(
System.IO.StreamReader streamReader)
{
string server;
bool blank = false;
bool interactive = !streamReader.BaseStream.CanSeek;
char[] delimiters = { ‘:’ };
string[] elements;

do {
if (blank)
Console.WriteLine();

if (interactive)
Console.Error.Write(“\nServerName[:PortNumber] > “);

server = streamReader.ReadLine();
if (server == null || // end of file – no more.
(interactive && server.Length==0)) // Interactive – blank line ends.
break;

if (server.StartsWith(“//”))
continue; // ignore comments – go to next server.

if (server.Contains(“:”))
{
elements = server.Split(delimiters);
server = elements[0];
port = Convert.ToInt32(elements[1]);
}

try
{
TestConnectionToSite(server,port);
Console.WriteLine();
}
catch (Exception e)
{
Console.Error.WriteLine(“Exception on site {0}”, server);
Console.Error.WriteLine(e.Message);
}
blank = true;
} while (true); // Forever – or until “break” hit.
}

public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine(“Subject: {0}”,certificate.Subject);
Console.WriteLine(“Issuer : {0}”,certificate.Issuer);
Console.WriteLine(“Serial : {0}”,certificate.GetSerialNumberString());
Console.WriteLine(“Expires: {0}”, certificate.GetExpirationDateString());

X509Certificate2 cert2 = new X509Certificate2(certificate);
if (cert2.NotAfter < DateTime.Now)
Console.WriteLine(“*** Certificate has expired!!!”);
else if (cert2.NotAfter.AddDays(-30.0) < DateTime.Now)
Console.WriteLine(“*** Certificate expires in thirty days or less!!!”);
if (sslPolicyErrors == SslPolicyErrors.None)
return true;

Console.WriteLine(“Certificate error: {0}”, sslPolicyErrors);

// Reject the SSL connection
return false;
}

private static void TestConnectionToSite(string serverName, int port)
{
Console.Error.WriteLine(“Connecting to server: ” + serverName + “:” + port.ToString());

TcpClient client = new TcpClient(serverName, port);

Console.Error.WriteLine(“Client connected.”);

// Create an SSL stream that will enclose the client’s stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);

// The server name must match the name on the server certificate.
try
{
sslStream.AuthenticateAsClient(serverName);
}
catch (AuthenticationException e)
{
// Uncomment this lot of code if you want to see the exceptions.
// Console.WriteLine(“Exception: {0}”, e.Message);
// if (e.InnerException != null)
// {
// Console.WriteLine(“Inner exception: {0}”, e.InnerException.Message);
// }
// Console.WriteLine(“Authentication failed – closing the connection.”);
client.Close();
return;
}
client.Close();

}

static void Usage()
{
Console.Error.WriteLine(“Usage:”);
Console.Error.WriteLine(
“SSLScan [filename] – if filename is blank, reads from stdin.”);
Console.Error.WriteLine(
“Scans multiple systems with an SSL connection,” +
” listing the certificates.”);
Console.Error.WriteLine(
“Sites are specified as host:port – the ‘:port’ part” +
” is optional, and if not”);
Console.Error.WriteLine(
“specified, will default to the previous port value,” +
” or the default of ” +
port.ToString());
}
}
}

Banks and SSL forms

I just knew this message was going to get badly diluted as it progressed.


What Ullrich has ‘discovered’ is that banks provide the form to their users over a plain-text link – while taking the input from the form using an SSL link.


This means that your password is not exposed to the Internet in clear-text, if you enter it into your bank’s form.


However, it means that you have nothing to prove that you are really connected to your bank’s form, other than a vague feeling that you typed in the right address, so anything that comes back must be from your bank.


With DNS hacks, and viruses that replace or edit your host file, that’s not a guarantee of anything very much, sadly – so these days, you should want your bank to identify themselves via a certificate – and that can only be done through an SSL link.


How do you know if the form on your screen has been delivered by SSL?  That’s what the ‘padlock’ icon shows:



The only problem… you also want your password to be sent back using SSL, and currently, there’s no browser that I am aware of that will tell you that this is the case, or prevent your form details from traveling back unprotected.


[It’s actually a computationally “hard” problem – possibly even computationally “impossible”, so let’s not be too down on the browser vendors.]

Two-factor authentication – what’s not to like?

Steve Riley always makes me think, sometimes so much that it hurts.  Thanks, Steve.  His latest blog posting is about two-factor authentication, and he’s asking for input on what you (we) want from it.


First, a couple of examples on authentication.



  1. “I am Bill Gates.” – this is not authentication.  It’s identification.  The fact that it’s an untrue claim doesn’t prevent it from being identification.
  2. “I am Bill Gates, this is Steve Ballmer, he’ll vouch for me.” – this is not authentication either.
  3. “I am Bill Gates, you already know Steve Ballmer, well he’ll vouch for me.” – this is weak authentication.
  4. “I am Bill Gates, you already know Steve Ballmer well, he’ll vouch for me.” – this is strong authentication.
  5. “I am Bill Gates – last time we spoke, I told you my favourite colour was red.” – this is authentication with a pre-shared secret.
  6. “I am Bill Gates – see, I still have the signed business card you gave me.” – this is authentication with a token.
  7. “I am Bill Gates – watch as I ignore the $1000 bill you left on the couch.” – this is authentication by ability (only a small number of people can afford to ignore free money).

There’s an old saying that goes something like “You can authenticate with something you are (biometrics), something you know (passwords), something you have (SecurID etc), or something you can do (skills measurement).”  Or, to put it another way, “something you used to be, something you have forgotten, something you lost, or something you can only do when relaxed in a well-lit room.”


The biggest deal we find with two-factor authentication is that the authentication device will be lost, destroyed, mangled, forgotten, given away as a prize at a sales talk, swallowed, or will simply refuse to operate in the Alaska (or Adis Ababa) office.


So, the second-factor (and if you’re replacing passwords with the factor, it’s not two-factor, it’s still one-factor!) has to be rapidly recoverable, re-deliverable, overridable, revokable (and ideally, unrevokable when they find it in their other trouser pocket), etc.  If I lose it, can you get me another one in the five minutes before I give my presentation?  [And if you can override it, what’s to prevent a hacker from doing the same?] n


Then you have to consider the message you send your staff by giving them security devices. “With these, your account is secure.” This means that they will use those skanky, dirty, disgusting computers in “Fly-By-Nite Internet Cafes Incorporated (Under New Management)”, or the clean ones at the airport that scream “definitely legitimate”, to download salary data on your most-valued executives, to view listings of covert agents in life-threatening deployments, to investigate your proctology results, etc.


What about those of us that wear multiple hats?  The consultants, the guys with an extra job?  How many tokens are we going to carry around with us?  One password per job is already fairly complexificated, but now you want us to remember a password _and_ carry around a half-dozen “key fobs”?  Perhaps a SecurID, Smart Card, or similar token should be able to authenticate against multiple servers – servers that don’t trust one another, and will not share keys.


Have I forgotten anything else you expect from a second-factor authentication method?

Signs your crypto is wrong.

Here are a few signs that you might be doing crypto the wrong way:



  1. You’re using a third-party library “because .NET keeps throwing exceptions”.
    Explanation: .NET’s cryptography routines throw exceptions when you are doing something wrong.  If you are getting exceptions, you need to figure out why.

  2. You are encrypting (or trying to encrypt) with the private key.
    Explanation: You encrypt with the public key, you decrypt with the private key.  You sign with the private key.  Yes, signing involves an encryption with the private key, but that’s the only time you should encrypt with the private key – and then, you should be doing the hash and sign together.  .NET will throw an exception if you try and encrypt data with a private key.

  3. You are decrypting (or trying to decrypt) with the public key.
    Explanation: see sign number 2.  If your protocol requires you to decrypt with the public key, except as part of a signature verification step, then it is broken.

  4. You are designing your own protocol, rather than copying from someone else.
    Explanation: Cryptography is hard to get right.  Cryptography needs to be done using published and analysed methods, to know that you are getting it right.  Everything you are trying to do with cryptography has already been done.  Copy from others.

  5. You have started writing your final program before finishing reading the book.
    Explanation: Most books discuss the simple concepts first, and the subtler concepts are left for further in the book.  If you start coding before you have met some of the subtleties, you will paint yourself into a corner, or you will release a half-finished piece of crypto.

  6. You are writing SSL code, but you do not know what “close_notify” means.
    Explanation: close_notify is part of the SSL protocol spec.  Without it, you don’t know if your session was interrupted by a forged closure.  It’s covered in chapter eight of the book I read.  See sign 5.

I expect to have more signs later.

More proof that crypto is harder than it needs to be.

I went looking today for a definitive statement on what purposes a certificate needs when it is created for an SMTP server that uses STARTTLS (I’m still looking, but I’m pretty certain I know what it needs).  I came across this gem of a piece from the Mac OS X guide to SSL:




The CSR and key are generated in the current directory, in a file called newreq.pem. When you enter:

cat newreq.pem

the system displays the file, which looks something like this:

—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,21F13B37A796482C

XIY0c7gnv0BpVKkOqXIiqpyONx8xqW67wghzDlKyoOZt9NDcl9wF9jnddODwv9ZU
A1UECxMPT25saW5lIFNlcnZpY2VzMRowGAYDVQQDExF3d3cuZm9yd2FyZC5jby56
YTBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQDT5oxxeBWu5WLHD/G4BJ+PobiC9d7S
6pDvAjuyC+dPAnL0d91tXdm2j190D1kgDoSp5ZyGSgwJh2V7diuuPlHDAgEDoAAw
DQYJKoZIhvcNAQEEBQADQQBf8ZHIu4H8ik2vZQngXh8v+iGnAXD1AvUjuDPCWzFu
QxS2zwfKG1u+YqS1c2v5ecBgqW78DQLvxMkpYU8+xge7vDeoYKE14w==
—–END RSA PRIVATE KEY—–

—–BEGIN CERTIFICATE REQUEST—–
MIIBPTCB6AIBADCBhDELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2Fw
ZTESMBAGA1UEBxMJQ2FwZSBUb3duMRQwEgYDVQQKEwtPcHBvcnR1bml0aTEYMBYG
A1UECxMPT25saW5lIFNlcnZpY2VzMRowGAYDVQQDExF3d3cuZm9yd2FyZC5jby56
YTBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQDT5oxxeBWu5WLHD/G4BJ+PobiC9d7S
6pDvAjuyC+dPAnL0d91tXdm2j190D1kgDoSp5ZyGSgwJh2V7diuuPlHDAgEDoAAw
DQYJKoZIhvcNAQEEBQADQQBf8ZHIu4H8ik2vZQngXh8v+iGnAXD1AvUjuDPCWzFu
pRUR8Z0wiJBeaqiuvTDnTFMz6oCq6htdH7/tvKhh
—–END CERTIFICATE REQUEST—–


Now, you can take this CSR to a Certificate Authority (CA) such as Thawte and Verisign. Using the CSR, you can purchase an SSL certificate from one of these CAs, and then use it to authenticate your email server.



Okay, so they just said that you should take the PEM file above to your CA, as the CSR?


That’s bad.


Why’s it bad?


Because that’s not just the CSR, it also holds the private key.  Think of the private keys as being your internal organs – nobody gets to have them while you’re still alive.


Sure, it’s encrypted, but did you really want to take the chance that the guys with all that cryptographic experience?


The Certificate Request, for the record, is exactly that portion between “—–BEGIN CERTIFICATE REQUEST—–” and “—–END CERTIFICATE REQUEST—–” – that’s the part (along with those two markers) that you should send to the CA.


This is pretty entertaining, but it doesn’t beat the training guides for Mirosoft’s Windows 2000 Official Curriculum course, which read “Alice encrypts a message using Bob’s private key”.  If Alice has access to Bob’s private key, they should be able to share secret messages at the breakfast table without encryption.

Why is PKI so hard? part 2

I promise, this one’s shorter.  It took me several hours to figure out, researching one alley or another, but here’s the deal:


I wanted to request a certificate from the local certificate server, running Windows 2000 Server.


I logged on to http://site.example.com/CertSrv/, and followed the form to request a certificate, as I have done dozens of times before.


Then I go to the Certification Authority Administrative Tool, pull up Pending Requests, and make it Issue the certificate I’ve just requested.


Now I go back to http://site.example.com/CertSrv/, and I select “Check on a pending certificate request”.


“You have no pending certificate requests.” says the web page.



Okay, I must have done something wrong.  Try again.


“You have no pending certificate requests.” it tells me again.


Hours go by, as I try any number of different ways of issuing the certificate request.


Eventually a random DejaNews search brings me to an article describing the exact problem.  It sounds stupid, but I try it and sure enough, I get my pending certificate request.



So, there’s the answer – and if you’re really good at “spot the difference” contests, you’ll notice that the answer is in the case of “CertSrv” in http://site.example.com/CertSrv – if you type it like that, your check on a pending certificate request will fail.


“certsrv” must be all in lower case for this to work.


Once again, PKI is “hard” because the tools we are given have this kind of rubbish in them.

Why is PKI so hard?

I’ve spent much of the last several years working on PKI – Public/Private Key Infrastructure – in one way or another.  But I’ve been doing it as a developer.  I’ve frequently found myself frustrated by incorrect documentation where I’ve had to deduce, from samples and trial-and-error programming, what the correct documentation should be.  I’m on a mission to beat Microsoft over the head with those incorrect pages until they get them updated and corrected.  [Yeah, I know, working for Microsoft, for free, is a dumb idea]


But partly, I expect that – developing PKI and related solutions, such as SSL and encryption, probably should be hard, because if you don’t understand it in depth, you will get it wrong [I’m dealing with a vendor right now that crows about “double encryption” and “encryption with the source’s private key”, which are usually strong signs that they “just don’t get it”].  Naively, I assumed that this pain of PKI was restricted to developers.


My new job changed all that.


I found myself tasked with publishing an Infopath Form on a Sharepoint site.  Easy enough, except that if you want the Form to do anything interesting, you have to give it “Full Trust” (a phrase that should put shivers up the spine of anyone working in security).  To make sure that your users fully trust the Form, you have to sign it, and you do that using a certificate whose purpose is to sign code (not document signing, because the Form is really code, not a document).


Great – that’s easy.


Your company should have a root certificate authority for enterprise certificates, and there should be an enterprise-wide certificate for code signing.  They’re easy to create.


The non-easy part comes next, as you realise what the user experience is going to be.



The user is going to visit the Sharepoint page hosting the form, and they’re going to see a dialog box with several buttons.  This dialog box warns them that something bad is going to happen to their computer, and warns them away from allowing it to happen.  When they obey that warning, the form doesn’t display, and they don’t get to do their job.


Should I solve this with user education?


No, because then I’m teaching my users that if they’re presented with this roadblock, they should simply click the magic buttons that let them see the naked dancing pigs.


I should solve this by deploying the certificate to my users’ desktops (not the users, because I don’t want them running this form or other signed code on the server machines!).


Easy – just follow the instructions.


Uh… what instructions?


In the interests of brevity, I’ll save you how long it took me to search for a document talking about deploying certificates, but I eventually figured out that what I was doing was the same as distributing a certificate for an Authenticode-signed application.  The right search brought me to a page on “Trusted Application Deployment Overview“.  Maybe by the time you click on it, Microsoft will have fixed the page, but it says (as do a number of other pages on this topic) “If you are deploying your application in a managed desktop environment; for example, a corporate intranet running the Windows operating system; you can add trusted publishers to a client’s store by creating a new certificate trust list (CTL) with Group Policy. For more information, see Create a certificate trust list for a Group Policy object


Oh, hey, that link doesn’t take you to anywhere like the article you want to read.  Here, let me fix that – Create a certificate trust list for a Group Policy object.  Great, now we’re ready to go.


So, we create a GPO, we set it to disabled (wouldn’t want to accidentally roll it out until we’re ready), and go ahead and add in our certificates.  In goes the root CA, with no problems.  Now for the code signing certificate.


What’s this?  “The certificate is the wrong type” – what’s that supposed to mean?


Off to the system at home to try it out there…  Same result, but apparently the message is clearer, perhaps because we’re on Windows SBS 2003.  The warning now tells me that I can only add a Certification Authority (CA) certificate to a Certificate Trust List (CTL).


Off I go to my first port of call, the Usenet newsgroups.  I post my question, and within a few hours, I get a helpful post back from Steven L Umbach, an MVP.  He points me to the articles for “Trusted Application Deployment Overview” and “Create a certificate trust list for a Group Policy object“.  Geez, what are they teaching these MVPs these days – I could have figured that out for myself 🙂


So, I call in a couple of favours with a few people I know at Microsoft that work with SSL.  Okay, so they didn’t actually owe me any favours, I just treat them as if they do, and they’re always really helpful.  I should send them a gift basket or something.  They’re a few layers removed from the admin side of things, so I don’t get much more than “we’ve passed you on to some other people that we know” for the next few days.


So, during the meanwhilst, I tinker.  I love to tinker.  That’s why I get into the scrapes that I do – but you learn so much from fixing the messes you get into, it’s worth it.  Eventually, I find a page that explains what the various certificate stores are supposed to be used for.  There, in the middle, is a single line in the table, describing Trusted Publishers:







Trusted Publishers Certificates from certification authorities that are trusted by Software Restriction policies.

“Software Restriction Policies” (SRP)?  Wow, that’s a whole different area from where I was looking.  SRP, also known within Microsoft as “SAFER” is a way to describe what software you will, or won’t, run.  It’s more of a policy tool than a security tool, in most cases, because if you restrict a program through SRP by, say, creating a rule that “Foxtrot.exe” is not allowed to run, it’s a piece of cake to rename it “Quickfire.exe” and have it work again.

But what SRP allows you to do that’s relevant to my story is to add a “Certificate Rule”.  This is where you state that “all code signed with such-and-such a certificate is immediately allowed”.  So I add a certificate rule, and associate it with my code signing certificate.

Whaddya know, the certificate is now magically in my “Trusted Publishers” certificate store!  What’s more, if I open the Certificate Manager (certmgr.msc), I see the certificate, but can’t delete it – to remove it, I have to delete the Certificate Rule in SRP.

And a Certificate Rule for SRP can be applied through a Group Policy Object.

A few hours later, I get a confirmation email from Microsoft…  “Alun is looking in the wrong direction, and should be thinking of Software Restriction Policies instead.  Oh, and he should have called PSS.”

“…should have called PSS.”  Yes, maybe, I might have gotten a quicker answer that way, but I have had a few frustrating experiences with PSS, where they read me the online help information, charged me my $$$, and sent me on my way.  I’d already tried the online help, it had been useless, pointing me in completely the wrong direction, and I didn’t anticipate PSS would do any better.  PSS, in my humble opinion, should be reserved for people who are unwilling to read enough of the help file and online documentation to understand how to solve their problem.  It should be a penalty for those who need a speedy response, and/or can’t be bothered to wade through documentation.

PSS should not be a penalty for having the temerity to do something where the documentation is clearly wrong, and was never tested against the programs it described.