Skip to content

Commit

Permalink
multiple slots
Browse files Browse the repository at this point in the history
  • Loading branch information
milad.bourhani committed Jun 14, 2024
1 parent 62e0575 commit 59cd302
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 28 deletions.
239 changes: 214 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

Minimal example of using PKCS#11 from Java without a real smart card.

# Setup
# Prerequisites

* Java 17+
* Maven 3

These instructions have been tested on MacOS using an M1 Mac Book Pro.

# Setup

This example uses [SoftHSM v2](https://github.com/opendnssec/SoftHSMv2) to
create a virtual PKCS#11-enabled smart card, and [OpenSC](https://github.com/OpenSC/OpenSC)
to interact with it (i.e., to import a private key and certificate into it).
Expand Down Expand Up @@ -37,29 +42,52 @@ slots.mechanisms = ALL
library.reset_on_fork = false
```

# Create a private key and certificate in PEM format
# Build

```bash
$ mvn clean install
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.753 s
[INFO] Finished at: 2024-06-14T12:35:24+02:00
[INFO] ------------------------------------------------------------------------
```

# Using a single slot

By default, PKCS#11 uses slot 0, and this line in `pkcs11.cfg` is optional:
```conf
slotListIndex = 0
```

First we will create a primary key and a certificate and import them in slot 0.

## Create a private key and certificate in PEM format

```bash
$ openssl genpkey -algorithm RSA -out private_key.pem
...............+......+.+++++++++++++++++++++++++++++++++++++++*...+.....+............+.+..+....+...+......+.....+....+...........+...............+..........+......+......+..+...+....+...+...+..............+......+.......+.....+.+..+++++++++++++++++++++++++++++++++++++++*.+......+..+......+......+.+............+......+..+......................+......+.....+.......+.....+...+....+.....+............+....+.....+....+..................+...+..+...+.........+.......+........+.......+......+..+.+.........+......+......+.....+...+.+.................+..................+.......+............+......+.........+......+.....+.+.....+.........+......+...+.+...........+.+.....+..........+...+..............+...+.........+..........+...........+...+......+...+......+.++++++
.....+......+........+++++++++++++++++++++++++++++++++++++++*...+....+...+........+.......+..+......+.......+........+....+..+++++++++++++++++++++++++++++++++++++++*.......+.+........+.+...........+.........+.........+.......+.....+.+...+...........+......+....+.....+....+.....+.+...+.....+.............+.................+.+..+.......+.....+...+....+...+...........+.......+............+..+...+.+.....+.+........+.+...........+...+.......+.....+.+...+......+........+...+....+.........+..+.......+........+....+...+...........+.+...+...+..+...+...+..........+...........+...+......+.......+........+.+......+........+................+........+.+..+...+.+........+.......+...+..+...............+.+..+...+...+.........+......+..........+.........+..+..................+...+.......+.....+.+............+..............+...+.+.........++++++
$ openssl req -new -key private_key.pem -x509 -days 365 -out certificate.pem -subj "/C=IT/ST=IT/L=BO/O=ACME/OU=IT/CN=example.com"
rm *.pem *.der
openssl genpkey -algorithm RSA -out private_key.pem
openssl req -new -key private_key.pem -x509 -days 365 -out certificate.pem \
-subj "/C=IT/ST=IT/L=BO/O=ACME/OU=IT/CN=example.com"
```

# Convert them into DER format
## Convert them into DER format

```bash
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
$ openssl x509 -in certificate.pem -outform DER -out certificate.der
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
openssl x509 -in certificate.pem -outform DER -out certificate.der
```

# Import them into a SoftHSM token
## Import them into a SoftHSM token

```bash
$ softhsm2-util --init-token --slot 0 --label "MyToken" --so-pin 1234 --pin 5678
The token has been initialized and is reassigned to slot 889004786

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so --login --pin 5678 --write-object private_key.der --type privkey --id 01 --label "MyPrivateKey"
$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --write-object private_key.der --type privkey --id 01 --label "MyPrivateKey"
Using slot 0 with a present token (0x34fd22f2)
Created private key:
Private Key Object; RSA
Expand All @@ -68,7 +96,8 @@ Private Key Object; RSA
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so --login --pin 5678 --write-object certificate.der --type cert --id 01 --label "MyCertificate"
$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --write-object certificate.der --type cert --id 01 --label "MyCertificate"
Using slot 0 with a present token (0x34fd22f2)
Created certificate:
Certificate Object; type = X.509 cert
Expand Down Expand Up @@ -96,30 +125,156 @@ Certificate Object; type = X.509 cert
ID: 01
```

# Sign a document from Java
## Sign a document from Java

```bash
$ mvn clean install
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.753 s
[INFO] Finished at: 2024-06-14T12:35:24+02:00
[INFO] ------------------------------------------------------------------------
$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.SignDocument pkcs11.cfg
Document signed successfully.
```

## Showing key store contents:

```bash
$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.LoadCertificates pkcs11.cfg
Alias found: MyCertificate
Private Key Algorithm: RSA
```

# Using two slots

This is an example of using multiple PKCS#11 slots, for example in the case
where one slot is used for signing, and another one for TLS authentication.

## Create a private key two certificates in PEM format

```bash
rm *.pem *.der
openssl genpkey -algorithm RSA -out private_key.pem
openssl req -new -key private_key.pem -x509 -days 365 -out certificate1.pem \
-subj "/C=US/ST=State/L=City/O=Organization/OU=Department/CN=example.com"
openssl req -new -key private_key.pem -x509 -days 365 -out certificate2.pem \
-subj "/C=US/ST=State/L=City/O=Organization/OU=Department/CN=example2.com"
```

## Convert them into DER format

```bash
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
openssl x509 -in certificate1.pem -outform DER -out certificate1.der
openssl x509 -in certificate1.pem -outform DER -out certificate2.der
```

## Import them into a SoftHSM token

```bash
$ softhsm2-util --init-token --slot 0 --label "MyToken" --so-pin 1234 --pin 5678
The token has been initialized and is reassigned to slot 1096489336

$ softhsm2-util --init-token --slot 1 --label "MyToken2" --so-pin 1234 --pin 5678
The token has been initialized and is reassigned to slot 2057173177

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --slot 1096489336 --write-object private_key.der --type privkey --id 01 --label "MyPrivateKey1"

Created private key:
Private Key Object; RSA
label: MyPrivateKey1
ID: 01
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --slot 1096489336 --write-object certificate1.der --type cert --id 01 --label "MyCertificate1"

Created certificate:
Certificate Object; type = X.509 cert
label: MyCertificate1
subject: DN: C=US, ST=State, L=City, O=Organization, OU=Department, CN=example.com
serial: 40DC6BE21B75C21C582FEEB26B40F5603F8D5D3B
ID: 01

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --slot 2057173177 --write-object private_key.der --type privkey --id 02 --label "MyPrivateKey2"

Created private key:
Private Key Object; RSA
label: MyPrivateKey2
ID: 02
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --slot 2057173177 --write-object certificate2.der --type cert --id 02 --label "MyCertificate2"

Created certificate:
Certificate Object; type = X.509 cert
label: MyCertificate2
subject: DN: C=US, ST=State, L=City, O=Organization, OU=Department, CN=example2.com
serial: 1415A68161F90B3F02D4FA79BF946A2BEA30EB82
ID: 02
```

This verifies that the private key and certificate have been imported:

```bash
$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --list-objects --slot 1096489336

$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.SignDocument
Private Key Object; RSA
label: MyPrivateKey1
ID: 01
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive
Certificate Object; type = X.509 cert
label: MyCertificate1
subject: DN: C=US, ST=State, L=City, O=Organization, OU=Department, CN=example.com
serial: 40DC6BE21B75C21C582FEEB26B40F5603F8D5D3B
ID: 01

$ pkcs11-tool --module /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so \
--login --pin 5678 --list-objects --slot 2057173177

Private Key Object; RSA
label: MyPrivateKey2
ID: 02
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive
Certificate Object; type = X.509 cert
label: MyCertificate2
subject: DN: C=US, ST=State, L=City, O=Organization, OU=Department, CN=example2.com
serial: 1415A68161F90B3F02D4FA79BF946A2BEA30EB82
ID: 02
```

## Sign a document from Java

```bash
$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.SignDocument pkcs11.cfg
Document signed successfully.
$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.SignDocument pkcs11_2.cfg
Document signed successfully.
```

## Showing key store contents:

```bash
$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.LoadCertificates pkcs11.cfg
Alias found: MyCertificate1
Private Key Algorithm: RSA

$ java -cp target/pkcs11-1.0-SNAPSHOT.jar org.example.LoadCertificates pkcs11_2.cfg
Alias found: MyCertificate2
Private Key Algorithm: RSA
```

# Clean up

```bash
$ softhsm2-util --show-slots
Available slots:
Slot 889004786
Slot 1096489336
Slot info:
Description: SoftHSM slot ID 0x34fd22f2
Description: SoftHSM slot ID 0x415b1978
Manufacturer ID: SoftHSM project
Hardware version: 2.6
Firmware version: 2.6
Expand All @@ -129,11 +284,45 @@ Slot 889004786
Model: SoftHSM v2
Hardware version: 2.6
Firmware version: 2.6
Serial number: fd3ebba434fd22f2
Serial number: c09bd013c15b1978
Initialized: yes
User PIN init.: yes
Label: MyToken
Slot 2057173177
Slot info:
Description: SoftHSM slot ID 0x7a9df8b9
Manufacturer ID: SoftHSM project
Hardware version: 2.6
Firmware version: 2.6
Token present: yes
Token info:
Manufacturer ID: SoftHSM project
Model: SoftHSM v2
Hardware version: 2.6
Firmware version: 2.6
Serial number: 761bd0367a9df8b9
Initialized: yes
User PIN init.: yes
Label: MyToken2
Slot 2
Slot info:
Description: SoftHSM slot ID 0x2
Manufacturer ID: SoftHSM project
Hardware version: 2.6
Firmware version: 2.6
Token present: yes
Token info:
Manufacturer ID: SoftHSM project
Model: SoftHSM v2
Hardware version: 2.6
Firmware version: 2.6
Serial number:
Initialized: no
User PIN init.: no
Label:
$ softhsm2-util --delete-token --token MyToken
Found token (89fc186a-637d-4396-f126-a42eab836a59) with matching token label.
The token (/opt/homebrew/var/lib/softhsm/tokens/89fc186a-637d-4396-f126-a42eab836a59) has been deleted.
$ softhsm2-util --delete-token --token MyToken2
...
```
3 changes: 1 addition & 2 deletions pkcs11.cfg
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
name = SoftHSM2
library = /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so
slotListIndex = 0
library = /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so
3 changes: 3 additions & 0 deletions pkcs11_2.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = SoftHSM2
library = /opt/homebrew/Cellar/softhsm/2.6.1/lib/softhsm/libsofthsm2.so
slotListIndex = 1
51 changes: 51 additions & 0 deletions src/main/java/org/example/LoadCertificates.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.example;

import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

public class LoadCertificates {

public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Must provide PKCS#11 conf file");
System.exit(1);
}

try {
// Load the PKCS#11 provider configuration
Provider p = Security.getProvider("SunPKCS11");
p = p.configure(args[0]);
Security.addProvider(p);

// Get an instance of the KeyStore
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, "5678".toCharArray());

// List aliases
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
System.out.println("Alias found: " + alias);

// Check if it is a certificate
if (ks.isCertificateEntry(alias)) {
X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
System.out.println("Certificate Subject: " + cert.getSubjectX500Principal());
}

// Check if it is a key entry
if (ks.isKeyEntry(alias)) {
Key key = ks.getKey(alias, null);
if (key instanceof PrivateKey) {
PrivateKey privateKey = (PrivateKey) key;
System.out.println("Private Key Algorithm: " + privateKey.getAlgorithm());
}
}
}

} catch (Exception e) {
e.printStackTrace();
}
}
}
7 changes: 6 additions & 1 deletion src/main/java/org/example/SignDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@
public class SignDocument {

public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.err.println("Must provide PKCS#11 conf file");
System.exit(1);
}

// Load the PKCS#11 provider configuration
Provider p = Security.getProvider("SunPKCS11");
p = p.configure("pkcs11.cfg");
p = p.configure(args[0]);
Security.addProvider(p);

// Get an instance of the KeyStore
Expand Down

0 comments on commit 59cd302

Please sign in to comment.