Skip to content

Commit

Permalink
Automatic PKCS#11 configuration for SafeNet eTokens
Browse files Browse the repository at this point in the history
  • Loading branch information
ebourg committed Nov 15, 2023
1 parent 42696ff commit 2bdef86
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 4 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Jsign is free to use and licensed under the [Apache License version 2.0](https:/
* Hashing algorithms: MD5, SHA-1, SHA-256, SHA-384 and SHA-512
* Keystores supported:
* PKCS#12, JKS and JCEKS files
* PKCS#11 hardware tokens ([YubiKey](https://www.yubico.com), [Nitrokey](https://www.nitrokey.com), etc)
* PKCS#11 hardware tokens ([YubiKey](https://www.yubico.com), [Nitrokey](https://www.nitrokey.com), [SafeNet eToken](https://cpl.thalesgroup.com/access-management/authenticators/pki-usb-authentication), etc)
* Cloud key management systems:
* [AWS KMS](https://aws.amazon.com/kms/)
* [Azure Key Vault](https://azure.microsoft.com/services/key-vault/)
Expand All @@ -51,6 +51,7 @@ See https://ebourg.github.io/jsign for more information.
#### Version 5.1 (in development)
* Signing of APPX/MSIX packages has been implemented (thanks to Maciej Panek for the help)
* Signing of Microsoft Dynamics 365 extension packages has been implemented
* SafeNet eToken support has been improved with automatic PKCS#11 configuration using the new `ETOKEN` storetype
* The certificate chain in the file specified by the `certfile` parameter can now be in any order
* VBScript, JScript and PowerShell XML files without byte order marks are now parsed as Windows-1252 instead of ISO-8859-1
* The format detection based on the file extension is now case insensitive (contributed by Mathieu Delrocq)
Expand Down
13 changes: 12 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ <h3 id="features">Features</h3>
<li>Keystores supported:
<ul>
<li>PKCS#12, JKS and JCEKS files</li>
<li>PKCS#11 hardware tokens (<a href="https://www.yubico.com">YubiKey</a>, <a href="https://www.nitrokey.com">Nitrokey</a>, etc)</li>
<li>PKCS#11 hardware tokens (<a href="https://www.yubico.com">YubiKey</a>, <a href="https://www.nitrokey.com">Nitrokey</a>, <a href="https://cpl.thalesgroup.com/access-management/authenticators/pki-usb-authentication">SafeNet eToken</a>, etc)</li>
<li>Cloud key management systems:
<ul>
<li><a href="https://aws.amazon.com/kms/">AWS KMS</a></li>
Expand Down Expand Up @@ -183,6 +183,7 @@ <h4 id="attributes" class="mobile-only">Attributes</h4>
<li><code>JCEKS</code>: SunJCE keystore (<code>.jceks</code> files)</li>
<li><code>PKCS12</code>: Standard PKCS#12 keystore (<code>.p12</code> or <code>.pfx</code> files)</li>
<li><code>PKCS11</code>: PKCS#11 hardware token</li>
<li><code>ETOKEN</code>: SafeNet eToken</li>
<li><code>NITROKEY</code>: Nitrokey HSM</li>
<li><code>OPENPGP</code>: OpenPGP card</li>
<li><code>OPENSC</code>: Smart card</li>
Expand Down Expand Up @@ -454,6 +455,7 @@ <h3 id="cli">Command Line Tool</h3>
- JCEKS: SunJCE keystore (.jceks files)
- PKCS12: Standard PKCS#12 keystore (.p12 or .pfx files)
- PKCS11: PKCS#11 hardware token
- ETOKEN: SafeNet eToken
- NITROKEY: Nitrokey HSM
- OPENPGP: OpenPGP card
- OPENSC: Smart card
Expand Down Expand Up @@ -531,6 +533,15 @@ <h4 id="example-nitrokey">Signing with a Nitrokey HSM</h4>
command). Keys without certificates are ignored. Alternatively, the OPENPGP storetype can also be used, it doesn't
require OpenSC and any key can be used by providing an external certificate.</p>

<h4 id="example-etoken">Signing with a SafeNet eToken</h4>

<p>Signing with a SafeNet eToken requires the installation of the
<a href="https://knowledge.digicert.com/general-information/how-to-download-safenet-authentication-client">SafeNet Authentication Client</a>.</p>

<pre>
jsign --storetype ETOKEN --storepass &ltPIN&gt; --certfile full-chain.pem application.exe
</pre>

<h4 id="example-smart-card">Signing with a smart card</h4>

<p>Signing with a smart card requires the installation of <a href="https://github.com/OpenSC/OpenSC">OpenSC</a>.</p>
Expand Down
1 change: 1 addition & 0 deletions jsign-cli/src/main/java/net/jsign/JsignCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static void main(String... args) {
+ "- JCEKS: SunJCE keystore (.jceks files)\n"
+ "- PKCS12: Standard PKCS#12 keystore (.p12 or .pfx files)\n"
+ "- PKCS11: PKCS#11 hardware token\n"
+ "- ETOKEN: SafeNet eToken\n"
+ "- NITROKEY: Nitrokey HSM\n"
+ "- OPENPGP: OpenPGP card\n"
+ "- OPENSC: Smart card\n"
Expand Down
11 changes: 11 additions & 0 deletions jsign-core/src/main/java/net/jsign/KeyStoreType.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,17 @@ void validate(KeyStoreBuilder params) {
Provider getProvider(KeyStoreBuilder params) {
return new SigningServiceJcaProvider(new HashiCorpVaultSigningService(params.keystore(), params.storepass(), getCertificateStore(params)));
}
},

/**
* SafeNet eToken
* This keystore requires the installation of the SafeNet Authentication Client.
*/
ETOKEN(false, true, true) {
@Override
Provider getProvider(KeyStoreBuilder params) {
return SafeNetEToken.getProvider();
}
};


Expand Down
113 changes: 113 additions & 0 deletions jsign-core/src/main/java/net/jsign/SafeNetEToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Copyright 2023 Emmanuel Bourg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.jsign;

import java.io.File;
import java.io.IOException;
import java.security.Provider;
import java.security.ProviderException;
import java.util.ArrayList;
import java.util.List;

import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Exception;

/**
* Helper class for working with SafeNet eTokens.
*
* @since 5.1
*/
class SafeNetEToken {

/**
* Returns the security provider for the SafeNet eToken.
*
* @return the SafeNet eTokens security provider
* @throws ProviderException thrown if the provider can't be initialized
*/
static Provider getProvider() {
return ProviderUtils.createSunPKCS11Provider(getSunPKCS11Configuration());
}

/**
* Returns the SunPKCS11 configuration of the SafeNet eToken.
*
* @throws ProviderException thrown if the PKCS11 modules cannot be found
*/
static String getSunPKCS11Configuration() {
File library = getPKCS11Library();
if (!library.exists()) {
throw new ProviderException("SafeNet eToken PKCS11 module is not installed (" + library + " is missing)");
}
String configuration = "--name=SafeNet eToken\nlibrary = \"" + library.getAbsolutePath().replace("\\", "\\\\") + "\"\n";
try {
long slot = getTokenSlot(library);
if (slot >= 0) {
configuration += "slot=" + slot;
}
} catch (Exception e) {
throw new ProviderException(e);
}
return configuration;
}

/**
* Returns the slot index associated to the token.
*/
static long getTokenSlot(File libraryPath) throws PKCS11Exception, IOException {
PKCS11 pkcs11 = PKCS11.getInstance(libraryPath.getAbsolutePath(), "C_GetFunctionList", null, false);
long[] slots = pkcs11.C_GetSlotList(true);
return slots.length > 0 ? slots[0] : -1;
}

/**
* Attempts to locate the SafeNet eToken PKCS11 library on the system.
*/
static File getPKCS11Library() {
String osname = System.getProperty("os.name");
String arch = System.getProperty("sun.arch.data.model");

if (osname.contains("Windows")) {
return new File(System.getenv("windir") + "/system32/eTPKCS11.dll");

} else if (osname.contains("Mac")) {
return new File("/usr/local/lib/libeTPkcs11.dylib");

} else {
// Linux
List<String> paths = new ArrayList<>();
if ("64".equals(arch)) {
paths.add("/usr/lib64/pkcs11/libeTPkcs11.so");
paths.add("/usr/lib64/libeTPkcs11.so");
paths.add("/usr/lib64/libeToken.so");
}
paths.add("/usr/lib/pkcs11/libeTPkcs11.so");
paths.add("/usr/lib/pkcs11/libeToken.so");
paths.add("/usr/lib/libeTPkcs11.so");
paths.add("/usr/lib/libeToken.so");

for (String path : paths) {
File library = new File(path);
if (library.exists()) {
return library;
}
}

return new File("/usr/local/lib/libeToken.so");
}
}
}
53 changes: 53 additions & 0 deletions jsign-core/src/test/java/net/jsign/SafeNetETokenTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright 2023 Emmanuel Bourg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.jsign;

import java.io.File;
import java.security.Provider;

import org.junit.Assume;
import org.junit.Test;

import static org.junit.Assert.*;

public class SafeNetETokenTest {

private void assumeSafeNetEToken() {
Assume.assumeTrue("SafeNet Authentication Client isn't installed",
new File(System.getenv("ProgramFiles") + "/OpenSC Project/OpenSC/pkcs11/opensc-pkcs11.dll").exists()
|| new File("/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so").exists());
}

@Test
public void testGetProvider() {
assumeSafeNetEToken();
try {
Provider provider = SafeNetEToken.getProvider();
assertNotNull(provider);
} catch (RuntimeException e) {
assertEquals("No PKCS11 token found", e.getCause().getMessage());
}
}

@Test
public void testGetLibrary() {
assumeSafeNetEToken();
File library = SafeNetEToken.getPKCS11Library();
assertNotNull(library);
assertTrue(library.exists());
}
}
2 changes: 1 addition & 1 deletion jsign-maven-plugin/src/main/java/net/jsign/JsignMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class JsignMojo extends AbstractMojo {
@Parameter( property = "jsign.storepass" )
private String storepass;

/** The type of the keystore (JKS, PKCS12, PKCS11, YUBIKEY, AWS, AZUREKEYVAULT, DIGICERTONE, ESIGNER or GOOGLECLOUD). */
/** The type of the keystore (JKS, PKCS12, PKCS11, ETOKEN, YUBIKEY, AWS, AZUREKEYVAULT, DIGICERTONE, ESIGNER or GOOGLECLOUD). */
@Parameter( property = "jsign.storetype" )
private String storetype;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ _jsign()
return 0
;;
--storetype)
COMPREPLY=( $( compgen -W 'JKS JCEKS PKCS12 PKCS11 AWS AZUREKEYVAULT DIGICERTONE ESIGNER GOOGLECLOUD HASHICORPVAULT YUBIKEY NITROKEY OPENPGP OPENSC' -- "$cur" ) )
COMPREPLY=( $( compgen -W 'JKS JCEKS PKCS12 PKCS11 AWS AZUREKEYVAULT DIGICERTONE ESIGNER ETOKEN GOOGLECLOUD HASHICORPVAULT YUBIKEY NITROKEY OPENPGP OPENSC' -- "$cur" ) )
return 0
;;
--storepass|-a|--alias|--keypass|-t|--tsaurl|-r|--tsretries|-w|--tsretrywait|-n|--name|-u|--url|-e|--encoding)
Expand Down
12 changes: 12 additions & 0 deletions jsign/src/deb/data/usr/share/man/man1/jsign.1
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ The type of the keystore:
.br
- PKCS11 : PKCS#11 hardware token
.br
- ETOKEN : SafeNet eToken
.br
- NITROKEY : Nitrokey HSM
.br
- OPENPGP : OpenPGP card
Expand Down Expand Up @@ -208,6 +210,16 @@ Keys without certificates are ignored. Alternatively, the OPENPGP storetype can
it doesn't require OpenSC and any key can be used by providing an external certificate.


.TP

Signing with a SafeNet eToken:

Signing with a SafeNet eToken requires the installation of the SafeNet Authentication Client
<a href="https://knowledge.digicert.com/general-information/how-to-download-safenet-authentication-client"></a>.</p>

jsign --storetype ETOKEN --storepass <PIN> --certfile full-chain.pem application.exe


.TP

Signing with a smart card:
Expand Down

0 comments on commit 2bdef86

Please sign in to comment.