The issue
The security of all IT systems can be compromised only through the interfaces between them and the world, so the security measures that protect the access to such systems are highly important. Administrators usually have higher privileges than average users, this is especially true for administrative access, that is one of the reasons why the old-fashioned telnet has been replaced by ssh.
Although the network traffic itself was thus protected by encryption, as long as the key of this encryption still depended on just some passwords -in the case of the PasswordAuthentication method- it was still vulnerable for dictionary attack, and as some kind of hash of this password had to be stored on server-side, it could be stolen and used for known-cyphertext attacks against the password.
The next step was using RSA private/public keypairs, where the public key was known by the server but the private one was kept as secret by the user. The user proved his identity by encrypting some server-generated nonce with his private key and the server verified it by decrypting it with the public half of the keypair: if the result matches the original nonce, it proves that the user has the private part of the keypair, and this confirms his identity (or defines it, depends on the viewpoint).
This way the user could be impersonated only by stealing his private key, something that exists only on his client computer, and even there only in a locally encrypted form, so the user has to (temporarily) decrypt it with the passphrase of the key before use. But what happens if this client computer gets stolen? If the passphrase is simple enough for a human to remember, it is usually simple enough for some cryptographic attack to crack. As the attacker now has the encrypted private key, he may attempt to decrypt it with as many candidate passphrases as he wants to, and sooner or later he would eventually succeed.
(OK, with a decent passphrase this later may outlast the Sun, but then it would be too complex to remember and even to enter frequently without mistakes.)
The only measures that can prevent such attacks are disabling access even to the encrypted private key and limiting the number of failed attempts before disabling it permanently. Exactly this is what SmartCards do: they contain some storage for the private and public keys (the latter ones embedded in X.509 Certificates) and a small microcontroller that actually performs all the tasks that require the private key. This way only the microcontroller has access to the private key, and as it is programmed to physically delete it after some number of failed passphrase attempts, this system can resist most security attacks. Considering that the encrypted private key is no longer available for anyone, the importance of passphrase complexity decreased dramatically: if the number of retries is low, even a 4-digit number will do, that is what the PIN number actually is for.
Our goal
Now that we have everything at hand, all we have to do is to put these subsystems together, and that is what this article is about. Our goal is to integrate and set up a bunch of 3rd-party tools to make our SmartCard available for authentication for SSH clients: first for PuTTY and second to Cygwin-based OpenSSH, while keeping the native Unix clients in mind as well.
What will we need
- A tool to interface to the SmartCard on the low level: the CryptoAPI (for Windows) or OpenCT (for Unix)
- A tool to handle the content on the SmartCard: OpenSC
- A tool that performs the authentication
- A tool for twiddling with certificates and such: OpenSSL
- And some glue script (in PowerShell or perl) that makes our life easier
Summary for the impatient
People tend to read the lengthy texts only when in trouble, so here are the essentials that will be described in details below:
- Download and install OpenSC for Windows
- Download and install OpenSSL for Windows
- Download PuTTYcard and extract it into your PuTTY installation directory, overwriting ‘pageant.exe’
- Download scard2ppk.ps1 into your PuTTY installation directory, (enable PowerShell script execution)
- Generate a reference .ppk file by inserting your card and running the scard2ppk script
- Add the resulting ‘SC_.ppk’ as private key to Pageant
- Cygwin only: download and install Charade
Setting up the low-level drivers
Windows users may just skip this, as the drivers for most of the recent card readers are already part of the OS.
Unix users shall install OpenCT, either from this link or from their distribution if it contains such a package.
Setting up OpenSC
OpenSC is an open-source project for accessing the contents of SmartCards in an organised manner, that is, not just manipulating some binary blobs, but interpreting them as certificates, keys and pin codes, with all the relationships between them.
This is fortunately OS-independent as OpenSC builds are available both for Unix and for Windows as well.
After downloading and installing it, we may test whether it really works or not:
Does it see the reader?
client$ ./opensc-tool.exe --list-readers Nr. Driver Features Name 0 pcsc Broadcom Corp Contacted SmartCard 0
What about the card?
client$ ./opensc-tool.exe --name Card not present.
Oops, insert it and try again:
client$ ./opensc-tool.exe --name Using reader with a card: Broadcom Corp Contacted SmartCard 0 TCOS
That is the archetype of my card, so it identified it correctly.
Let’s see what kind of card we have here:
client$ ./pkcs11-tool.exe --list-slots Available slots: Slot 4294967295 Virtual hotplug slot (empty) Slot 1 Broadcom Corp Contacted SmartCard 0 token label: NetKey Card (NetKey PIN0) token manuf: TeleSec GmbH token model: PKCS#15 emulated token flags: readonly, login required, PIN initialized, token initialized, other flags=0x40000 serial num : 9017230002684482 Slot 2 Broadcom Corp Contacted SmartCard 0 token label: NetKey Card (NetKey PIN1) ... Slot 3 Broadcom Corp Contacted SmartCard 0 token label: NetKey Card (SigG PIN) ... Slot 4 Broadcom Corp Contacted SmartCard 0 (empty)
Nice, let’s see what we have on these slots:
client$ ./pkcs11-tool.exe --slot 1 --list-objects Certificate Object, type = X.509 cert label: Verschluesselungs Zertifikat 1 ID: 46 Public Key Object; RSA 1024 bits label: Verschluesselungs Zertifikat 1 ID: 46 Usage: encrypt, verify Certificate Object, type = X.509 cert ... Public Key Object; RSA 1024 bits ... client$ ./pkcs11-tool.exe --slot 2 --list-objects Certificate Object, type = X.509 cert label: Signatur Zertifikat 1 ID: 45 Public Key Object; RSA 1024 bits label: Signatur Zertifikat 1 ID: 45 Usage: encrypt, verify Certificate Object, type = X.509 cert ... client$ ./pkcs11-tool.exe --slot 3 --list-objects Certificate Object, type = X.509 cert ... Public Key Object; RSA 1024 bits ...
Nice, on object-level we see everything we need, but PKCS11 tells nothing about the purpose of these objects, like which private key belongs to which certificate, etc.
For this we will use a tool one level higher:
client$ ./pkcs15-tool.exe --list-certificates X.509 Certificate [Signatur Zertifikat 1] ... X.509 Certificate [Telesec Signatur Zertifikat] ... X.509 Certificate [Verschluesselungs Zertifikat 1] Flags : 2 Authority: no Path : df0143b1 ID : 46 Encoded serial: 02 03 04307E X.509 Certificate [Telesec Verschluesselungs Zertifikat] ... client$ ./pkcs15-tool.exe --list-keys Private RSA Key [Signatur Schluessel] ... Private RSA Key [Verschluesselungs Schluessel] Com. Flags : 1 Usage : [0x7], encrypt, decrypt, sign Access Flags: [0x1D], sensitive, alwaysSensitive, neverExtract, local ModLength : 1024 Key ref : 129 Native : yes Path : df0153b1 Auth ID : 03 ID : 46 Private RSA Key [Authentifizierungs Schluessel] ... client$ ./pkcs15-tool.exe --list-pins PIN [PIN] Com. Flags: 0x3 ID : 01 Flags : [0x51], case-sensitive, initialized, unblockingPin Length : min_len:6, max_len:16, stored_len:16 Pad char : 0x00 Reference : 0 Type : ascii-numeric Path : 5000 Tries left: 3 PIN [PUK] ... PIN [NetKey PIN0] Com. Flags: 0x3 ID : 03 Flags : [0x13], case-sensitive, local, initialized Length : min_len:6, max_len:16, stored_len:16 Pad char : 0x00 Reference : 128 Type : ascii-numeric Path : df015080 Tries left: 0 PIN [NetKey PIN1] ...
So that is it, everything parsed OK, all info displayed, except the content.
If we reached this far, at least we can read and interpret the content of our SmartCard.
Setting up OpenSSL
OpenSSL is an open-source library and a command-line wrapper for performing virtually all types of cryptographic tasks, en- and decrypting data, generating various hashes and signatures, manipulating certificates and so on.
We need it because when generating the reference .ppk file for the SmartCard later, we will have to invent some meaningful but unique name for it. Although we have already encountered plenty of numerical identifiers that are unique, but long numbers are just not convenient enough for file names, so we have to find something better. As the certificates are issued to subjects that are also supposed to be unique, using some part of such a subject field (eg. the common name or the e-mail address) is evident. However, for obtaining these subject fields we have to interpret the internal structure of the certificates, and this is what we use OpenSSL for.
First of all, let’s install it either from source or from its Windows installer.
This latter one needs the Visual C++ 2008 Redistributables and the OpenSSL ‘Light’ flavour, both of which is available both for 32 and 64-bit systems here.
Just as an example, let’s retrieve one of those certificates above (ID=46):
client$ ./pkcs15-tool.exe --read-certificate 46 -----BEGIN CERTIFICATE----- MIIGCTCCBPGgAwIBAgIDBDB+MA0GCSqGSIb3DQEBBQUAMIGDMQswCQYDVQQGEwJE ... wF18JPu9a550wtqbIA== -----END CERTIFICATE-----
And interpret it:
client$ ./pkcs15-tool.exe --read-certificate 46 | \ > openssl.exe x509 -noout -text Using reader with a card: Broadcom Corp Contacted SmartCard 0 Certificate: Data: Version: 3 (0x2) Serial Number: 274558 (0x4307e) Signature Algorithm: sha1WithRSAEncryption Issuer: C=DE, O=T-Systems Enterprise Services GmbH, OU=Trust Center Deutsche Telekom, CN=T-Systems Company CA 2 Validity Not Before: Oct 4 09:03:53 2010 GMT Not After : Oct 4 23:59:00 2013 GMT Subject: C=HU, O=T-Systems International GmbH, OU=Hungary, OU=SubBuda01.Hungary, OU=IT Services Hungary Szolgaltato, CN=Simon Gabor/emailAddress=G-Simon@t-systems.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (1024 bit) Modulus: 00:91:5c:9d:02:67:8a:46:23:e5:62:1b:fe:26:93: ... 51:98:21:45:3c:20:9d:0c:53 Exponent: 1073741953 (0x40000081) X509v3 extensions: X509v3 Authority Key Identifier: keyid:BD:55:0E:CC:8B:31:F7:2D:B4:BA:4A:C9:08:46:AE:CC:2B:B9:95:8F X509v3 Key Usage: Digital Signature, Key Encipherment X509v3 Subject Key Identifier: 00:D5:44:15:15:31:58:29:71:5C:C2:A4:1A:13:83:EB:05:03:22:9B X509v3 Certificate Policies: Policy: 1.3.6.1.4.1.7879.13.16 CPS: http://kryptsi.telesec.de/cgi-bin/caservice/krypTSI/DownloadPDF/krypTSI-CPS.pdf X509v3 Subject Alternative Name: othername:, email:GABOR.SIMON2@IT-SERVICES.HU, email:G-Simon@t-systems.com X509v3 CRL Distribution Points: Full Name: URI:http://kryptsi.telesec.de/cgi-bin/caservice/krypTSI/af_DownloadCRL.crl?-crl_format=X_509&-ident-issuer_name=T-Systems%20Company%20CA%202 Full Name: URI:ldap://ldap-krypTSI.telesec.de/cn=T-Systems%20Company%20CA%202,ou=Trust%20Center%20Deutsche%20Telekom,o=T-Systems%20Enterprise%20Services%20GmbH,c=DE?CertificateRevocationList X509v3 Extended Key Usage: critical TLS Web Client Authentication, Microsoft Smartcardlogin X509v3 Basic Constraints: critical CA:FALSE Signature Algorithm: sha1WithRSAEncryption 10:fb:b6:1d:f8:83:56:f8:e4:09:ae:0e:db:15:74:e9:a9:f5: ... c2:da:9b:20
(By the way, am I not afraid of just dumping content from my SmartCard out to a public blog? Remember: certificates contain only public keys, so if you asked me a question by e-mail and I answered it, my mail would be signed or encrypted with these and so the mail would contain exactly these certificates, otherwise you would not be able to verify my identity.
It is this easy to get my certs, so it is just perfectly safe to publish them, this is exactly what they are intended for.
On the contrary, private keys would be the most sensitive kind of information, but I do not have to worry about not publishing them even by accident, because the SmartCard will not let them out, not even after entering the correct PIN codes.)
Anyway, as we have OpenSSL installed and operational, we may proceed to the next step.
Setting up the Pageant/PuTTYcard ssh agent
This is a Windows-only section, so the Unix users may just skip it.
Interconnecting separately developed systems is never easy, and this was the case for the PuTTY and OpenSC as well.
There is a sub-project of OpenSC for such integration (Smart Card Bundle), and among its ‘other software’ products there is one item that we will need: PuTTYcard.
As the concept of ssh agent will be frequently referred to, it is time to tell a few words about it. However, if you are familiar with the subject, you may just want to skip the following sub-chapter.
The basics of SSH authentication
As we discussed in the preface, the most simple authentication method is the PasswordAuthentication. It works (more or less) like this:
- the client connects to the server and they establish a secure tunnel (this alone is a worth a post, but it is off-topic here)
- the client asks the user for his password and sends it to the server
- the server checks it against some backend datastore, and closes the connection if it does not match
Its drawbacks have already been discussed, and as the secret (the password) is to travel through the tunnel, it is completely useless for us now.
The next was the PubkeyAuthentication:
- the client connects, secure tunnel gets established
- the server invents a random nonce and sends it to the client
- the client asks the user for his passphrase
- the client opens the encrypted private key file (like ~/.ssh/id_rsa) and decrypts it using the passphrase
- the client encodes the nonce using the private key and sends the result to the server
- the server tries to decode the received blob using the authorised public keys (like lines of ~/.ssh/authorized_keys)
- if any of them results the original nonce it sent to the client, then the user is considered authenticated
For a one-step connection it would be just fine, but consider the case when you want to jump on from the remote server to a third machine: for this you would need either another keypair (private key on the ‘server’, public on the ‘third machine’), or you would have to store your private key on the ‘server’ as well.
Neither of these is really a good option, the best scenario would be if the ‘server’ -knowing that you are not locally there- when it gets the nonce from the ‘third machine’, would just pass on to your client for encoding, and then would do the same for the encrypted blob:
- the server connects to the third machine, secure tunned gets established
- the third one invents a nonce and sends it to the server
- the server passes the nonce to the client
- the client asks the user for his passphrase
- the client opens the encrypted private key file and decrypts it using the passphrase
- the client encodes the nonce using the private key and sends the result to the server
- the server passes the encoded blob to the third machine
- the third machine tries to decode the received blob using the authorised public keys
- if any of them results the original nonce it sent to the client, then the user is considered authenticated
Sounds good, just the functionality of getting a nonce encoded somehow has to be separated from managing the SSH connection. When this nonce-encoding happens on the machine at which the user is physically sitting, it shall be done by asking him a passphrase, opening a private key and encoding the nonce with it. When it happens on a machine where the user has just logged into via SSH, it shall be done by passing on the nonce to the previous hop, and then passing the results back.
This user-asking nonce-encoder program is called SSH Agent and the process of passing the nonce back and forth is called SSH Agent Forwarding. Technically it goes like this:
- on the local machine the SSH Agent published somehow its access. On Unix machines this means listening on a Unix-domain socket (like ‘/tmp/ssh-ngMYP12762/agent.12762′) and exporting the name of it in the environment variable SSH_AUTH_SOCK.
On Windows machines the Pageant process can be located by its window name and communicated to by IPC messages and shared memory blocks. - when you log on to a Unix server and your client requested the Agent Forwarding, the sshd on the remote side also opens a listening Unix-domain socket
- whenever you run an ssh client on the server, it sees this SSH_AUTH_SOCK variable so it does not try to encode the nonce itself, but send it there for having it encoded
- the sshd that is listening on the socket passes the nonce through the SSH connection to the previous client
- your local client also passes the nonce to what it sees in the local SSH_AUTH_SOCK, that is, to the Agent
- … and the same route for the result, just backwards
Installing Pageant/PuTTYcard
As this tool is in fact a replacement of the regular PuTTY-supplied Pageant, the installation is a bit different from the usual ‘next-next-finish’ method.
You shall open the downloaded PuTTYcard-*.zip archive and unpack two files from it to your PuTTY install location: PAGEANT.EXE and PuTTYiso7816.dll. The .dll is a new file, but a PAGEANT.EXE shall already exist there, so it is a good idea to rename the old one to some sensible name instead of just overwriting it.
Now that the software is ready for use, we have to tell it what to use for authentication from our SmardCard.
Creating the .PPK file
As we have seen, there are quite a bunch of objects on our SmartCard: several certificates, private keys, pin codes, so the questions are which of them to use and how to tell this to Pageant.
Normally Pageant would load your private keys from plain files that have a ‘.ppk’ extension, this is true for the PuTTYcard version as well, the extra functionality of it is that it recognises a special syntax in these .ppk files and if found, it treats the file not as a private key, but a name of a .dll to use for accessing the SmartCard and some numbers that identify the certificate, the private key and the pin code to use.
This special syntax means a textual line in the format of “PuTTYcard,PuTTYis7816.dll,path,key_ref,pin_ref,cert_suffix”.
The first item is just a magic marker string, the second is the name of the .dll, but the rest is the result of a somewhat complex process described here, that involves heavy use of the pkcs15-tool that you have just installed.
Although for those who like this sort of thing, it may be the sort of thing they like, most people just would like to use this technology and avoid the internal details if possible. Good news for them: because the process is in fact straightforward data manipulation only, scripts could be written to do it: scard2ppk.ps1 using PowerShell and scard2ppk.pl for the Unix users.
After moving these .ppk files to your Pageant directory, all you have to do is to tell Pageant to make use of them:
- manually: right-click on the Pageant icon in the notification area, choose ‘Add key’ and select your .ppk file
- automatically: locate the shortcut that starts Pageant on system startup (usually in Start menu/Programs/Startup/Pageant), right click on it and open its Properties, then edit the Target field and add the names of your .ppk files to the end of the command line.
You can verify whether it works by right-clicking on the Pageant icon in the notification area and choosing ‘View keys’: if you see something like ‘ssh-rsa 1024 e8:ed:…:5f Key DF01:43B1,81/80′ (the end contains the identifiers in your .ppk file), then it works fine.
From this point on, if a PuTTY, PSCP or plink session tries to ask Pageant to use your SmartCard, it will display a popup window for your PIN code.
Image may be NSFW.
Clik here to view.
Configuring your public key on Unix servers
It is good to know that your SSH client now tries to use the private keys on your SmartCard, but in order to get it working, the servers shall know about the public keys they should accept.
For this, log in to the server using your password (do not forget to turn AgentForwarding on in your client), and then simply ask the SSH toolkit about what forwarded public keys does it actually see:
server$ ssh-add -L ssh-rsa AAAAB3NzaC1yc2EAAAAEQAAAgQAAAIEAkVydAmeKRiPlYhv+JpOLUrCvd9/P6eTKL20+3r4pXDql4sGVbFHF9Dl2HM5CnpZGXeNeGpIm/no8bmkxt3uZaJz1ItlZN1PPYdObIeCCm7JJ3zHBUkV4oC5e+562snhMUjxuEiwlS1wClYnMt1huDs//j02n/VpRmCFFPCCdDFM= Key DF01:43B1,81/00
What you see here is the public key stored within the certificate whose private key you have told Pageant to use, exactly in the same format expected by sshd, so you may just append it to the current public key collection:
mkdir ~/.ssh 2>/dev/null; ssh-add -L >>~/.ssh/authorized_keys
Log out and log in again, this time you shall be logged on automatically using PubKeyAuthentication.
(Note: Do not forget your password though, as there are system services (eg. sudo) that will still need it…)
Installing Charade
Now that PuTTY users are using their SmartCards successfully, we have one last thing to do: make it available for the users of the good old commandline OpenSSH clients on Cygwin.
Fortunately this problem has already occured to someone else as well, and that is what the Charade project is for: it provides an ‘ssh-agent’-compatible interface for the Cygwin environment and uses a Pageant process as backend.
Download the tarball release, extract it to some temporary location and run the ‘install.sh’ within it. It will replace the original ‘/usr/bin/ssh-agent’ with a symlink to the ‘charade’ executable, so everything shall work with it as previously with the original agent.
Now all authentication attempts of Cygwin processes (like ‘svn checkout svn+ssh://…’) will use the OpenSSH-style ssh-agent through $SSH_AUTH_SOCK, that is currently Charade, which will communicate with Pageant and let it do the job.
To test this part, restart the ssh-agent the way you have configured it (usually an agent is started for each interactive session, so just opening a new terminal will do), and then ask ‘ssh-add’ about the public keys it can access:
server$ ssh-add -L ssh-rsa AAAAB3NzaC1yc2EAAAAEQAAAgQAAAIEAkVydAmeKRiPlYhv+JpOLUrCvd9/P6eTKL20+3r4pXDql4sGVbFHF9Dl2HM5CnpZGXeNeGpIm/no8bmkxt3uZaJz1ItlZN1PPYdObIeCCm7JJ3zHBUkV4oC5e+562snhMUjxuEiwlS1wClYnMt1huDs//j02n/VpRmCFFPCCdDFM= Key DF01:43B1,81/00
Missing things
There are a few uncovered points in this story that perhaps may be added later:
- Setting up OpenCT on Unix systems
- Last time I actually tried it (~2 years ago), OpenCT did not yet know the PCI IDs of my reader, therefore some patching had to be done to the source, so at that state I would not have recommended it for production use as a mature technology. In the IT field ‘two years ago’ is a synonym for ‘back in the historical times’, so it may very well be working out-of-the-box now. If you happen to have a Unix box with a decent card reader, it is definitely worth a try, if you can get it to the point when ‘pkcs11-tool’ sees your card, then the rest shall work as well.
- Getting ‘ssh-agent’ to use the SmartCard on Unix systems
- This Charade + Pageant/PuTTYcard trick is nice, but not available on Unixes, at least because of that ‘.dll’ thing. However, the manpage of ‘ssh-add’ states that it can ‘−s pkcs11: Add keys provided by the PKCS#11 shared library pkcs11′.
If I were to set this up, I would start trying by ‘ssh-add -s /usr/lib/pkcs11/opensc-pkcs11.so’, although I do not have the slightest idea how could I tell ‘ssh-add’ about which cert+key+pin combination I want to use. This whole issue seems kind of …not_quite_mature_yet… to me. - Choosing the right cert+key+pin combination
- I have found that some combinations work and some do not, although all of them seemed valid. So, I had to add some heuristics, like ignoring keys whose ‘Tries left’ field is zero, ignoring keys whose ‘Usage’ does not contain ‘encrypt’, and ignoring certs whose ‘Flags’ is zero. Just listing the certs using ‘pkcs15-tool -c’ it seemed that there were more than one certs on my card with the same ID, the only way to dump them distinctively was by their Paths using ‘opensc-explorer’, so I could verify them via ‘openssl’. One of them was in base64, the other was in an ASN1 userCertificate (OID=2.5.4.36), with the same pubkey but with everything (issuer, x509 extensions, validity, subject, etc.) different. Kind of confusing…
Image may be NSFW.
Clik here to view.
Clik here to view.
