VW 7.4 Spoilers - X.509

#smalltalk

(Republished from Cincom Smalltalk Tech Tips)

Considerable amount of time in this release cycle was spent on improving our ASN.1 support. I don’t want to get into details of this rather technical topic (maybe next time), but I’d like to show how much we’ve gained from it. As it happens ASN.1 is one of the principal building blocks of many security related standards, for example the PKCS suite of standards, CMS and S/MIME and obviously the ITU-T’s X-series of recommendations where ASN.1 itself comes from (X.208). As you’ve probably guessed the X.509 certificate standard is part of the X-series as well but it is also published as RFC 3280. Anyway, enough with the barrage of acronyms, my point is, it’s very useful to have it. So let’s get back to X.509 certificates.

VisualWorks has supported X.509 certificates for a while, however until now it was the bare minimum necessary to conduct an SSL handshake. To be fair that’s still quite a bit of functionality because you need to be able to decode pretty much any certificate, model all aspects of a certificate, validate all kinds of certificate properties and certificate chains etc. But it’s still a far cry from full X.509 support. The most glaring limitation was inability to generate and encode certificates. With the new ASN.1 framework, we were able to rip out pretty much all the special purpose marshaling code from the X.509 framework and in exchange get not only the ability to decode but also to encode certificates. So here’s how you can generate a self signed certificate with VW7.4.

An X.509 certificate binds a “name” of a subject to a “public key”. The certificate itself is signed by the issuer of the certificate and the “name” of the issuer must also be present on the certificate. So to create the certificate we’ll need the name of the subject, the name of the issuer and subject’s public key. A self signed certificate is one where the issuer and the subject is the same entity, therefore the subject and issuer names are the same. Self-signed certificates are commonly used for certificate authority (CA) certificates, because they conveniently bundle the autority name with their public key. So let’s define the name:

caName := Security.X509.Name new
			add: 'C' -> 'US';
			add: 'L' -> 'Cincinnati';
			add: 'O' -> 'Cincom Systems';
			add: 'OU' -> 'Cincom Smalltalk';
			add: 'CN'-> 'Test Certificate Authority';
			yourself.

A name in X509 is actually a collection of so called AttributeValueAssertions where the keys represent well known aspects of a name. The various attributes are C=Country, L=Location, O=Organization, OU=Organization Unit, CN=Common Name, etc. Yes, the APIs could be nicer, but we’re not there yet and I want to present code that actually works, so please, bear with me.

Now, we’ll also need keys. Let’s say we want the CA to use RSA keys, so here’s how to generate some.

caKeys := Security.RSAKeyGenerator keySize: 1024.
caKeys publicKey

Note that key generation is actually a fairly expensive process so depending on your hardware this may take a few seconds. I’m purposely not storing the public key, because the generator will cache both keys until it’s flushed. At this point I just wanted to trigger key generation so that we’re done with it.

Now we are ready to create the Certificate. There are few more required attributes like the serial number and validity dates, but those are self explanatory.

ca := Security.X509.Certificate new
		serialNumber: 1000;
		issuer: caName;
		subject: caName;
		notBefore: Date today;
		notAfter: (Date today addDays: 7);
		publicKey: caKeys publicKey;
		forCertificateSigning;
		yourself.

The #forCertificateSigning bit is necessary for a CA certificate, but let’s not worry about that now. We’re almost done. The last missing bit on our shiny new certificate is the signature. The magic spell for that is #signUsing: which takes and instance of a signing algorithm preinitialized with a key as the argument. Of course the key has to be the private key, but you already knew that, right ?

	ca signUsing: (
		Security.RSA new
			useSHA;
			privateKey: caKeys privateKey;
			yourself).

And there it is. If you ask the certificate for its #printOpenSSLString, you should get something like this:

Certificate:
	Data:
		Version: 3 (0x2)
		Serial Number: 
			03:e8
		Signature Algorithm: sha-1WithRSAEncryption
		Issuer: C=US, L=Cincinnati, O=Cincom Systems, OU=Cincom Smalltalk, CN=Test Certificate Authority
		Validity
			Not Before: Dec 13 00:00:00 2005 GMT
			Not After : Dec 20 00:00:00 2005 GMT
		Subject: C=US, L=Cincinnati, O=Cincom Systems, OU=Cincom Smalltalk, CN=Test Certificate Authority
		Subject Public Key Info:
			Public Key Algorithm: rsaEncryption
			RSA Public Key: (1024 bits)
				Modulus (1024 bit):
					00:e5:4e:70:0d:65:7f:11:98:a3:2c:37:5a:0a:6d:
					ab:8f:28:92:fc:f9:db:f7:9c:1a:fa:01:a5:96:95:
					24:da:1c:ad:6b:18:65:cd:96:66:dd:e3:90:c8:2a:
					f6:62:ba:03:04:ec:ed:e0:db:f6:ab:65:93:84:4c:
					ef:94:12:a2:cb:14:b5:f2:15:c1:cf:37:9f:fb:e4:
					3a:ae:5e:3f:fb:9f:21:71:15:de:b8:20:c8:e8:8d:
					59:28:bf:ae:85:35:1a:9b:81:3f:b3:cc:d5:35:a1:
					da:3f:2e:dc:ca:cb:38:5e:33:a5:98:cf:7d:9f:2e:
					3e:99:ce:22:0f:21:26:24:37
				Exponent: 65537 (0x10001)
		X509v3 extensions:
			KeyUsage: critical
			X509v3 Basic Constraints: critical
			CA:TRUE
	Signature Algorithm: sha-1WithRSAEncryption
		97:dc:d6:dd:6f:d1:ce:08:d4:f8:d6:5f:bd:70:f1:ac:6a:7a:
		96:58:8c:b0:29:db:2b:82:43:6b:a3:f5:72:55:f5:c2:42:80:
		2c:4a:da:99:e0:be:e8:dd:55:df:45:69:8c:64:d8:5d:bf:78:
		42:0b:2a:89:19:3b:ae:b8:fa:db:b5:66:f3:1f:84:2a:e8:ab:
		09:5f:e6:48:03:25:60:98:a4:29:42:a1:6d:1e:69:82:b9:81:
		63:d1:3e:23:74:df:a2:cd:e3:c5:ca:a3:d6:da:1a:67:37:8b:
		50:cf:16:47:e6:17:ae:df:2b:a4:56:6e:06:58:58:c2:b4:24:
		ab:37

Now this gives you a complete certificate expressed as a Smalltalk object, but how can you share it with the rest of the world? Well that’s where our new encoding capability gets involved.

	marshaler := ASN1.DERStream on: (ByteArray new: 100).
	marshaler marshalObject: ca withType: ca asn1Type.
	marshaler contents

The result of this is a byte array representing the DER encoding of the certificate. Now to prove that I’m not making all this up, let’s see how OpenSSL likes our new certificate. OpenSSL comes with this handy little utility called, surprisingly, openssl. You can usually find OpenSSL pre-installed on most Unix based platforms and if you’re using Windows you should have had cygwin installed on it already. So give the following a try as well.

First we need to save the DER bytes into a file. The certificate actually caches its DER encoding so we don’t need to invoke the above again, the following will suffice:

	'TestCA.der' asFilename writeStream
		binary;
		nextPutAll: ca encoding source;
		close

After this you should have a file TestCA.der in your image directory. To run it through OpenSSL, execute the following in your favourite shell:

	openssl x509 -inform DER -in TestCA.der -text

With any luck you should get the following in response.

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1000 (0x3e8)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, L=Cincinnati, O=Cincom Systems, OU=Cincom Smalltalk, CN=Test Certificate Authority
        Validity
            Not Before: Dec 13 05:00:00 2005 GMT
            Not After : Dec 20 05:00:00 2005 GMT
        Subject: C=US, L=Cincinnati, O=Cincom Systems, OU=Cincom Smalltalk, CN=Test Certificate Authority
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:e5:4e:70:0d:65:7f:11:98:a3:2c:37:5a:0a:6d:
                    ab:8f:28:92:fc:f9:db:f7:9c:1a:fa:01:a5:96:95:
                    24:da:1c:ad:6b:18:65:cd:96:66:dd:e3:90:c8:2a:
                    f6:62:ba:03:04:ec:ed:e0:db:f6:ab:65:93:84:4c:
                    ef:94:12:a2:cb:14:b5:f2:15:c1:cf:37:9f:fb:e4:
                    3a:ae:5e:3f:fb:9f:21:71:15:de:b8:20:c8:e8:8d:
                    59:28:bf:ae:85:35:1a:9b:81:3f:b3:cc:d5:35:a1:
                    da:3f:2e:dc:ca:cb:38:5e:33:a5:98:cf:7d:9f:2e:
                    3e:99:ce:22:0f:21:26:24:37
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha1WithRSAEncryption
        97:dc:d6:dd:6f:d1:ce:08:d4:f8:d6:5f:bd:70:f1:ac:6a:7a:
        96:58:8c:b0:29:db:2b:82:43:6b:a3:f5:72:55:f5:c2:42:80:
        2c:4a:da:99:e0:be:e8:dd:55:df:45:69:8c:64:d8:5d:bf:78:
        42:0b:2a:89:19:3b:ae:b8:fa:db:b5:66:f3:1f:84:2a:e8:ab:
        09:5f:e6:48:03:25:60:98:a4:29:42:a1:6d:1e:69:82:b9:81:
        63:d1:3e:23:74:df:a2:cd:e3:c5:ca:a3:d6:da:1a:67:37:8b:
        50:cf:16:47:e6:17:ae:df:2b:a4:56:6e:06:58:58:c2:b4:24:
        ab:37
-----BEGIN CERTIFICATE-----
MIICjzCCAfigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMCVVMx
EzARBgNVBAcTCkNpbmNpbm5hdGkxFzAVBgNVBAoTDkNpbmNvbSBTeXN0ZW1zMRkw
FwYDVQQLExBDaW5jb20gU21hbGx0YWxrMSMwIQYDVQQDExpUZXN0IENlcnRpZmlj
YXRlIEF1dGhvcml0eTAeFw0wNTEyMTMwNTAwMDBaFw0wNTEyMjAwNTAwMDBaMHsx
CzAJBgNVBAYTAlVTMRMwEQYDVQQHEwpDaW5jaW5uYXRpMRcwFQYDVQQKEw5DaW5j
b20gU3lzdGVtczEZMBcGA1UECxMQQ2luY29tIFNtYWxsdGFsazEjMCEGA1UEAxMa
VGVzdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ0wCwYJKoZIhvcNAQEBA4GNADCB
iQKBgQDlTnANZX8RmKMsN1oKbauPKJL8+dv3nBr6AaWWlSTaHK1rGGXNlmbd45DI
KvZiugME7O3g2/arZZOETO+UEqLLFLXyFcHPN5/75DquXj/7nyFxFd64IMjojVko
v66FNRqbgT+zzNU1odo/LtzKyzheM6WYz32fLj6ZziIPISYkNwIDAQABoyQwIjAP
BgNVHQ8BAf8EBQMDB4QAMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
gYEAl9zW3W/RzgjU+NZfvXDxrGp6lliMsCnbK4JDa6P1clX1wkKALErameC+6N1V
30VpjGTYXb94QgsqiRk7rrj627Vm8x+EKuirCV/mSAMlYJikKUKhbR5pgrmBY9E+
I3Tfos3jxcqj1toaZzeLUM8WR+YXrt8rpFZuBlhYwrQkqzc=
-----END CERTIFICATE-----

So this is what it takes to generate a certificate in VW7.4. The APIs definitely need more work, but it’s not that bad, huh? I think that even with a fairly minimalistic UI slapped on top of this it should be possible to run a private PKI hierarchy. Actually, I guess I should show how to “issue” a certificate.

Issuing a certificate is nothing more than generating a new certificate for a given subject with subject’s public key and signed using issuer’s private key. So we need subject name and keys.

	subjectName := Security.X509.Name new add: 'CN' -> 'Test Subject'; yourself.
	subjectKeys := Security.DSAKeyGenerator keySize: 1024.
	subjectKeys publicKey

The certificate is created as previously, note however that it is important to express the usage of the associated key properly. So let’s say the keys for this certificates can be used for signing data and not other certificates (you can find more examples in the ‘accessing - key usage’ protocol on Certificate).

	subject := Security.X509.Certificate new
			serialNumber: 1000;
			issuer: caName;
			subject: subjectName;
			notBefore: Date today;
			notAfter: (Date today addDays: 7);
			publicKey: subjectKeys publicKey;
			forSigning;
			yourself.

And finally the signature.

	subject signUsing: (
		Security.RSA new
			useSHA;
			privateKey: caKeys privateKey;
			yourself).

We’ll leave encoding of this new certificate as an exercise for the attentive reader. Thanks for reading this far, I hope you enjoyed the article.