PGP data encryption for use with Yubico OpenPGP Smart Card
Asked Answered
S

1

13

I'm trying to implement PGP encryption based on Yubikey NEO OpenPGP Smart Card applet in a Java application. It seems to be a dark art and is not easy to google this stuff but here is where I got so far:

  1. The card is initialized, keys are generated using gpg tool. It generally works. I have my public key in .asc format and managed to load it into org.bouncycastle.openpgp

  2. Connect to the smart card in the USB dongle using javax.smartcardio APIs.

  3. Select the OpenPGP applet

    val pgpAID = bytes(0xD2, 0x76, 0x00, 0x01, 0x24, 0x01)
    val answer = cardChannel.transmit(CommandAPDU(0x00, 0xA4, 0x04, 0x00, pgpAID))
    
  4. Successfully present the right PIN to the card

    val pin = "123456"
    return bytes(0x00, 0x20, 0x00, 0x82, pin.length) + pin.toByteArray(Charsets.UTF_8)
    
  5. Send a quasi-successful (see below) decipher command

    bytes(0x00, 0x2a, 0x80, 0x86, data.size) + data + bytes(0x00)
    

    When data = "xxxx".toByteArray(), the result is SW=9000 (= success) but no data is returned. It's a naive test because the OpenPGP applet documentation on page 52 mentions that

    the command input (except padding indicator byte) shall be formatted according to PKCS#1 before encryption.

I have no idea how to encrypt the data and get it into PKCS#1 format.

I also tried reading through Yubico OpenPGP card implementation tests but it only provides another "failing" example (line 196). I tried running that but the result is different: the test expects SW=0050 (indicating an exception?) and what I get is SW=6f00 (No precise diagnosis, according to this document).

I created a GitHub repository with the entire code. It's written in Kotlin but should be easy to read.

Schroth answered 21/11, 2015 at 8:36 Comment(1)
Oh, you already got a question here. Note that you may want to connect using T=1 protocol instead of T=0, and that not all cards support extended length. Otherwise you may have to look into ISO/IEC 7816-4 command chaining.Maroc
D
6

Your question is somewhat confused, but I'm pretty sure you want to create PGP encrypted messages using an RSA publickey corresponding to the RSA privatekey on your smartcard, and then use the RSA privatekey on the smartcard to (help) decrypt them. PGP (like practically everything else) uses hybrid encryption, so a PGP encrypted message in relevant part consists of:

  • the actual message encrypted with an appropriate symmetric algorithm like TDES or AES using a randomly-generated working key, call it K
  • the working key K plus some metadata encrypted by RSA using the recipient's publickey and the padding defined by the original PKCS#1 standard, now officially called RSAES-PKCS1-v1_5 but still widely referred to somewhat imprecisely as PKCS1.

You shouldn't need to do the encryption steps because any software that implements the standard can do so, including GnuPG or BouncyCastle's bcpg library. If you want to do it yourself, perhaps for test data using a faked K and no real message, you need to do the padding and RSA modular exponentiation; in Java, at least Oracle or openjdk Java with standard crypto providers, you can just use a javax.crypto.Cipher obtained with .getInstance("RSA/ECB/PKCS1Padding") in the usual way.

"PKCS1" encryption padding (for RSA) is as described at the bottom of page 52 and top of page 53 of that document, which is identical in content though not format to the current OpenPGP spec (and earlier), which refers to and is effectively identical to near-current PKCS#1 spec (and earlier), all of which say it is:

  • one byte 00
  • one byte 02
  • enough bytes nonzero random to make the result the correct length and be secure
  • one byte 00
  • the "plaintext", which for PGP encryption is actually the working symmetric key K formatted as specified in the PGP spec.

Note the paragraph beginning

In case of the AES algorithm

appears to be for a different option, not PGP AFAICS, described on the previous page as

By option (announced in Extended capabilities) the card supports the decryption of a plain text with an AES-key stored in a special DO (D5). This is useful if no certificate or public key exists and the external world has a common secret with the card.

so ignore it.

Dibru answered 22/11, 2015 at 20:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.