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:
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
Connect to the smart card in the USB dongle using javax.smartcardio
APIs.
Select the OpenPGP applet
val pgpAID = bytes(0xD2, 0x76, 0x00, 0x01, 0x24, 0x01)
val answer = cardChannel.transmit(CommandAPDU(0x00, 0xA4, 0x04, 0x00, pgpAID))
Successfully present the right PIN to the card
val pin = "123456"
return bytes(0x00, 0x20, 0x00, 0x82, pin.length) + pin.toByteArray(Charsets.UTF_8)
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.
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:
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With