Saturday, July 11, 2009

Using OpenSSL to create a certificate authority on Windows

I've used OpenSSL to create certificates for testing on my windows machines. I'll outline the basic steps I followed to setup OpenSSL and use it to sign certificate requests. I found Dylan Beattie's how-to very useful in getting this working.

These instructions assume Windows XP, and that you have some basic knowledge of the IIS web server.

First, install OpenSSL. You can download a Windows installer and binaries here.

Initial Setup
Create a working directory, we'll assume c:\openSSL\work. Then create the following folder structure:




work/
keys/
requests/
certs/
Create a config file, openssl.conf in the work directory using this content:




# SSLeay example configuration file.
# This is mostly being used for generation of certificate requests.
#

RANDFILE = .rnd

####################################################################

[ ca ]
default_ca = CA_default # The default ca section

####################################################################
[ CA_default ]

certs = certs # Where the issued certs are kept
crl_dir = crl # Where the issued crl are kept
database = database.txt # database index file.
new_certs_dir = certs # default place for new certs.

certificate = cacert.pem # The CA certificate
serial = serial.txt # The current serial number
crl = crl.pem # The current CRL
private_key = private\cakey.pem # The private key
RANDFILE = private\private.rnd # private random number file

x509_extensions = x509v3_extensions # The extentions to add to the cert
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = md5 # which md to use.
preserve = no # keep passed DN ordering

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match

# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = match
commonName = supplied
emailAddress = optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (eg, your website's domain name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 40

[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20

[ x509v3_extensions ]
# under ASN.1, the 0 bit would be encoded as 80
# nsCertType = 0x40
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
#nsCertSequence
#nsCertExt
#nsDataType


Create an empty database.txt file in the work directory.

Create a serial.txt file in the work directory that contains the following (the numbers 01 followed by RETURN):


01



Create Certificate Authority Stuff

  1. Create a 1024-bit private key for use in creating the CA (this will prompt you for a password; remember it, as you'll need it when you're signing certs!):
    ..\bin\openssl genrsa -des3 -out keys\ca.key 1024
  2. Create a master certificate based on this key (for use in signing other certs):
    ..\bin\openssl req -config openssl.conf -new -x509 -days 1001 -key keys\ca.key -out certs\ca.cer
  3. Export the CA cert in a DER file for windows users to import into their Trusted Root Store:
    ..\bin\openssl x509 -in certs\ca.cer -outform DER -out certs\ca.der

At this point your CA should now be setup.

Create a batch file for handling certificate requests

Since you'll presumably be handling many certificate requests, here's a windows batch file to automate the process (we'll name it "ca_create_server_cert.bat"):



@echo off
REM This batch file is used to create server certificates from certificate request files.
REM USAGE: ca_create_server_cert.bat [inputfilename] [outputfile]
REM If either of the command line paramters are missing, you will be prompted for it.

SET basedir=c:\openssl\
if "%1"=="" (
SET /P requestfile="Enter certificate request filename (should already be in %basedir%\work\requests): "
) ELSE (
SET requestfile=%1
)

if "%2"=="" (
SET /P outputfile="Enter output filename (with no extension): "
) ELSE (
SET outputfile=%2
)

REM change to the work directory
cd %basedir%\work

echo requestfile=%requestfile%
echo outputfile=%outputfile%
echo binpath=%binpath%

REM create the certificate
%basedir%bin\openssl ca -policy policy_anything -config openssl.conf -cert certs\ca.cer -in requests\%requestfile% -keyfile keys\ca.key -days 730 -out certs\%outputfile%.cer.TMP

REM convert it to an x509 format cert for IIS
%basedir%bin\openssl x509 -in certs\%outputfile%.cer.TMP -out certs\%outputfile%_x509.cer

echo If there were no error messages, the new certificate is located in:
echo %basedir%work\certs\%outputfile%_x509.cer

Signing a server certificate request

We'll use IIS as the server in this example.

  • First create the server certificate request in IIS
  • Place the request file in the "requests" directory
  • Run ca_create_server_cert.bat and follow the prompts
  • Take the output certificate (.cer) file and install it in IIS



Friday, July 10, 2009

Easy management of windows certificates

Yes, you can access certificate management through IE via tools--internet options--content--certificates, but there's a quicker way:
  1. Start--Run--type "MMC" and hit [ENTER]
  2. File--Add/Remove Snap-In
  3. Click the Add button
  4. Select "Certificates" and click Add
  5. Select "My User Account" and click Finish
  6. Select "Certificates" and click Add
  7. Select "Computer Account" and click Finish
  8. Click OK
  9. Your window should now display certificates for the current user and the computer
  10. Use File--Save As and save it in Administrative Tools (the default) as Certificates.msc.
Now you'll have the Certificates.msc under Administrative Tools (in your Start menu) that will bring you straight to this screen:

Sunday, June 28, 2009

Creating Custom Data Generators in Visual Studio 2008

Recently I was working on creating some test data in Visual Studio 2008. I created a Data Generation Plan, and used the Regular Expression Data Generator quite a bit, but found myself wanting more options to create data. VS2008 has that option available: write some custom Data Generators.

I quickly headed down that path, which was pretty straightforward. But when it came time to install my custom data generators into VS2008, I ran into problems. Apparently there are multiple versions of VS2008 out there, some of which expect the DLL and XML files in different places. A question I posted on the MSDN Forums resulted in an answer that proved to be quite helpful.

I was using VS2008 Team Suite, and here's the process I used:

Create a new project
The first step is to create a new VS2008 DLL project. In that project, create your custom data generator, such as this sample custom date generator:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TeamSystem.Data.DataGenerator;

namespace CustomDataGenerators
{
///
/// This class generates random DateTimes in the specified range.
/// User specifies the StartingDays (the number of days to add or subtract to today's
/// date to get the beginning of the range) and the DateRange (number of days past
/// that date in which random DateTimes will be selected).
///
public class DateGenerator : Generator
{
private DateTime _rndDate;
private DateTime _secondDate;
private int _startDays = -30;
private int _dayRange = 60;
private int _daysToSecondDate = 30;
private int _secondDateDayRange = 0;
Random _rnd = null;

private Random RandomObject
{
get
{
if (_rnd == null)
_rnd = new Random(base.Seed);
return _rnd;
}
}

[Input(Name="Starting Days",
Description="Number of days to add (or subtract) from today's date to set the bottom end of the date range",
DefaultValue=-30)]
public int StartingDays
{
set { _startDays = value; }
get { return _startDays; }
}

[Input(Name = "Days in Range",
Description = "Number of days in the date range",
DefaultValue = 60)]
public int DateRange
{
set { _dayRange = value; }
get { return _dayRange; }
}

[Input(Name = "Second Date Days",
Description = "Number of days after the first date for the second date",
DefaultValue = 30)]
public int DaysToSecondDate
{
set { _daysToSecondDate = value; }
get { return _daysToSecondDate; }
}

[Input(Name = "Second Date Random Days",
Description = "Number of days after 'Second Date Days' to create a range in which the Second Date will be randomized. Leave at zero for a set second date.",
DefaultValue = 0)]
public int DayRangeSecondDate
{
set { _secondDateDayRange= value; }
get { return _secondDateDayRange; }
}

[Output(Name="Output", Description="A random date/time string in the specified date range")]
public DateTime Output
{
get { return _rndDate; }
}

[Output(Name="SecondDate", Description="A second date/time string X number of days past the first random output date")]
public DateTime SecondDate
{
get { return _secondDate; }
}

protected override void OnGenerateNextValues()
{
_rndDate = DateTime.Now;
_rndDate = _rndDate.AddDays(_startDays);
int randomHours = RandomObject.Next(0, _dayRange*24);
_rndDate = _rndDate.AddHours(randomHours);
_secondDate = _rndDate.AddDays(_daysToSecondDate);
if (_secondDateDayRange != 0)
{ //randomize the 2nd date
int hrs2 = RandomObject.Next(0, _secondDateDayRange * 24);
_secondDate = _secondDate.AddHours(hrs2);
}
}
}
}
Compile the project to make sure it works.

Write the XML descriptor
Add an XML file to your project, name it DLLNAME.extensions.xml (where DLLNAME is the exact filename of your DLL, without the ".DLL") and structure it as follows:

<extensions assembly="CustomDataGenerators, Version=1.1.0.0, Culture=neutral, PublicKeyToken=95510968c816b5ce" version="1" xmlns="urn:Microsoft.VisualStudio.TeamSystem.Data.Extensions" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="urn:Microsoft.VisualStudio.TeamSystem.Data.Extensions Microsoft.VisualStudio.TeamSystem.Data.Extensions.xsd">
<extension type="CustomDataGenerators.DateGenerator" enabled="true">
</extension>

If you create more data generator classes, add a new "" tag for each class.

Copy the DLL and XML file into your VS2008 application directory
Now it's time to copy the DLL and the extensions XML file into the VS2008 application directory so that VS2008 can use them in your data generation plan. In my case, I have CustomDataGenerators.DLL (should be in bin\debug under your project directory) and CustomDataGenerators.Extensions.XML. They go into the following directories (assuming that you installed VS2008 in c:\Program Files):

CustomDataGenerators.Extensions.xml: c:\Program Files\Microsoft Visual Studio 9.0\DBPro
CustomDataGenerators.DLL: c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\Private Assemblies


Restart VS2008 and use your custom data generator
VS2008 needs to be restarted for it to see the custom data generator. If there's a problem with it, you should get an error message. Otherwise, open your data generation plan and you should have your custom data generator(s) available in the drop downs for appropriate fields.

Saturday, June 27, 2009

Beginnings...

Over my years in the tech industry, I've benefited from tips I've found on countless blogs, usually through googling a specific problem I was facing. I've also found that after I solve a problem, if I don't have to deal with it again for a couple months, I've usually forgotten exactly what I did to solve it.

So I'm starting this blog to give me a place to record my thoughts in a place that has better retention than my memory! And perhaps others will find the solutions and musings occasionally useful as well.