Search code examples
opensslredhatsmartcardapdupkcs#11

Extract names from PIV smartcard


I am trying to extract the following from a PIV Smartcard:

  1. Subject Common Name
  2. Certificate Subject Alt Name / Microsoft Principal Name

I am using RedHat 6 (eventually 7) and CoolKey as my PKCS11 module.

I need a way to extract this information via code without requiring the smartcard pin, be it from shell commands or a smartcard library. Currently I can get the Common Name by using the shell command 'pkcs11-tools --module -T' so the Subject Alt Name is truly what I am after, but I would like to find a better way to get the Common Name if available.

I know this information is available without entering the pin as I can view it all in the included Smartcard Manager on RHEL (esc). I have a certificate chain of root, intermediate, and subordinate if that matters.

My thoughts are I have to extract the certificate from the card, verify that certificate with my local CAs, and then decrypt it. I have spent days reading documentation on APDUs, smartcards, and openssl and have gotten nowhere.

edit view of RHEL smart card manager: enter image description here

This is what the smart card viewer shows when you open the card and view the details. The Microsoft Principal Name is what I'm looking to extract from the card, as well as the "common name" which is displayed in the Hierarchy portion as well as other spots, shown by the red text.

I actually have since switched to using pkcs15-tool, as pkcs11-tool cutoff longer common names (you can see this in the title bar of the screenshot, same issue). Output of: 'pkcs15-tool --list-info'

Using reader with a card: <reader name>
PKCS#15 Card [LASTNAME.FIRSTNAME.MIDDLENAME.12345678]:
        Version          : 0
        Serial number    : <big string>
        Manufacturer ID  : piv_II
        Flags            :

My current method is simply parsing the string in brackets as the common name and having users enter the Alt Name manually using the Redhat smartcard tool.


Solution

  • I was able to get the common name and UPN/Certificate Alt Name by using Java bouncycastle and the iaik pkcs11 wrapper.

    //path to .so library file
    Module pkcs11Module = Module.getInstance(this.getProperties().getProperty("PKCS11_LIBRARY"));
    pkcs11Module.initialize(null);
    Slot[] slotsWithToken = pkcs11Module.getSlotList(Module.SlotRequirement.TOKEN_PRESENT);
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    for(Slot s : slotsWithToken)    {
        Session session = s.getToken().openSession(Token.SessionType.SERIAL_SESSION, Token.SessionReadWriteBehavior.RO_SESSION, null, null);
        session.findObjectsInit(new X509PublicKeyCertificate());
        Object[] objects = null;
        while((objects = session.findObjects(1)).length > 0)    {
            for(Object c : objects) {
                X509PublicKeyCertificate cert = (X509PublicKeyCertificate) c;
                byte[] certValue = cert.getValue().getByteArrayValue();
                Certificate cc = certFactory.generateCertificate(new ByteArrayInputStream(certValue));
                if(cc instanceof X509Certificate)   {
                    X509Certificate x509 = (X509Certificate) cc;
                    //COMMON NAME:
                    String name = new X500Name(x509.getSubjectDN().getName()).getRDNs()[0].getFirst().getValue().toString();
                    Collection<List<?>> altNames = x509.getSubjectAlternativeNames();
                    for(List<?> list : altNames)    {
                        ASN1Sequence seq = ASN1Sequence.getInstance(new ASN1InputStream(new ByteArrayInputStream((byte[]) list.get(1))).readObject());
                        ASN1TaggedObject obj = (ASN1TaggedObject) seq.getObjectAt(1);
                        //ALT NAME:
                        String upn = obj.getObject().toString();
                        upn = upn.substring(upn.lastIndexOf("]") + 1);
                    }
                }
            }
        }
    }
    

    The upn has '[0]' appended in front of it and I'm not sure why that is, so I operate on it to get rid of that.