Skip to content

Commit

Permalink
Fixed JsignJcaProvider to support the PKCS11 based keystores (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
ebourg committed Jan 5, 2024
1 parent 11c8c2d commit 94fcfd5
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 35 deletions.
61 changes: 61 additions & 0 deletions jsign-core/src/main/java/net/jsign/jca/AbstractSignatureSpi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* 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.jca;

import java.security.InvalidParameterException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;

/**
* Base class for JCA signature implementations.
*
* @since 5.1
*/
public abstract class AbstractSignatureSpi extends SignatureSpi {

protected final String signingAlgorithm;

public AbstractSignatureSpi(String signingAlgorithm) {
this.signingAlgorithm = signingAlgorithm;
}

@Override
protected void engineInitVerify(PublicKey publicKey) {
throw new UnsupportedOperationException();
}

@Override
protected boolean engineVerify(byte[] sigBytes) {
throw new UnsupportedOperationException();
}

@Override
protected void engineUpdate(byte b) throws SignatureException {
engineUpdate(new byte[]{b}, 0, 1);
}

@Override
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
throw new UnsupportedOperationException();
}

@Override
protected Object engineGetParameter(String param) throws InvalidParameterException {
throw new UnsupportedOperationException();
}
}
85 changes: 83 additions & 2 deletions jsign-core/src/main/java/net/jsign/jca/JsignJcaProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@

import java.io.InputStream;
import java.security.AccessController;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.util.Enumeration;
Expand All @@ -45,6 +49,13 @@
* provider.configure(vaultname)
* KeyStore keystore = KeyStore.getInstance(AZUREKEYVAULT.name(), provider);
* keystore.load(null, accessToken);
*
* PrivateKey key = (PrivateKey) keystore.getKey(alias, null);
*
* Signature signature = Signature.getInstance("SHA256withRSA", provider);
* signature.initSign(key);
* signature.update("Lorem ipsum dolor sit amet".getBytes());
* signature.sign();
* </pre>
*
* @since 5.1
Expand All @@ -64,7 +75,7 @@ public JsignJcaProvider() {
for (DigestAlgorithm digest : DigestAlgorithm.values()) {
if (digest != DigestAlgorithm.MD5) {
String algorithm = digest.name() + "with" + alg;
putService(new ProviderService(this, "Signature", algorithm, SigningServiceSignature.class.getName(), () -> new SigningServiceSignature(algorithm)));
putService(new ProviderService(this, "Signature", algorithm, JsignJcaSignature.class.getName(), () -> new JsignJcaSignature(algorithm)));
}
}
}
Expand Down Expand Up @@ -108,7 +119,7 @@ public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyEx
builder.keypass(new String(password));
}
try {
return getKeyStore().getKey(alias, password);
return new JsignJcaPrivateKey((PrivateKey) getKeyStore().getKey(alias, password), builder.provider());
} catch (UnrecoverableKeyException e) {
e.printStackTrace(); // because jarsigner swallows the root cause and hides what's going on
throw e;
Expand Down Expand Up @@ -142,4 +153,74 @@ public void engineLoad(InputStream stream, char[] password) {
}
}
}

static class JsignJcaPrivateKey implements PrivateKey {

private final PrivateKey privateKey;
private final Provider provider;

public JsignJcaPrivateKey(PrivateKey key, Provider provider) {
this.privateKey = key;
this.provider = provider;
}

@Override
public String getAlgorithm() {
return privateKey.getAlgorithm();
}

@Override
public String getFormat() {
return privateKey.getFormat();
}

@Override
public byte[] getEncoded() {
return privateKey.getEncoded();
}

public PrivateKey getPrivateKey() {
return privateKey;
}

public Provider getProvider() {
return provider;
}
}

static class JsignJcaSignature extends AbstractSignatureSpi {

private Signature signature;

public JsignJcaSignature(String signingAlgorithm) {
super(signingAlgorithm);
}

@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
JsignJcaPrivateKey key = (JsignJcaPrivateKey) privateKey;

try {
signature = Signature.getInstance(signingAlgorithm, key.getProvider());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
signature.initSign(key.getPrivateKey());
}

@Override
protected void engineUpdate(byte b) throws SignatureException {
signature.update(b);
}

@Override
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
signature.update(b, off, len);
}

@Override
protected byte[] engineSign() throws SignatureException {
return signature.sign();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,23 @@
package net.jsign.jca;

import java.security.GeneralSecurityException;
import java.security.InvalidParameterException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;

class SigningServiceSignature extends SignatureSpi {
class SigningServiceSignature extends AbstractSignatureSpi {

private final String signingAlgorithm;
private SigningServicePrivateKey privateKey;
private byte[] data;

public SigningServiceSignature(String signingAlgorithm) {
this.signingAlgorithm = signingAlgorithm;
}

@Override
protected void engineInitVerify(PublicKey publicKey) {
throw new UnsupportedOperationException();
super(signingAlgorithm);
}

@Override
protected void engineInitSign(PrivateKey privateKey) {
this.privateKey = (SigningServicePrivateKey) privateKey;
}

@Override
protected void engineUpdate(byte b) {
throw new UnsupportedOperationException();
}

@Override
protected void engineUpdate(byte[] b, int off, int len) {
data = new byte[len];
Expand All @@ -62,19 +48,4 @@ protected byte[] engineSign() throws SignatureException {
throw new SignatureException(e);
}
}

@Override
protected boolean engineVerify(byte[] sigBytes) {
throw new UnsupportedOperationException();
}

@Override
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
throw new UnsupportedOperationException();
}

@Override
protected Object engineGetParameter(String param) throws InvalidParameterException {
throw new UnsupportedOperationException();
}
}
2 changes: 1 addition & 1 deletion jsign-core/src/test/java/net/jsign/YubikeyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

public class YubikeyTest {

private void assumeYubikey() {
public static void assumeYubikey() {
Assume.assumeTrue("libykcs11 isn't installed",
new File(System.getenv("ProgramFiles") + "/Yubico/Yubico PIV Tool/bin/libykcs11.dll").exists()
|| new File("/usr/lib/x86_64-linux-gnu/libykcs11.so").exists());
Expand Down
23 changes: 22 additions & 1 deletion jsign-core/src/test/java/net/jsign/jca/JsignJcaProviderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import net.jsign.DigestAlgorithm;
import net.jsign.KeyStoreType;
import net.jsign.YubikeyTest;

import static org.junit.Assert.*;

Expand All @@ -48,7 +49,7 @@ public void testServices() {
}

@Test
public void testKeyStore() throws Exception {
public void testKeyStoreSigningService() throws Exception {
JsignJcaProvider provider = new JsignJcaProvider("https://cs-try.ssl.com");

KeyStore keystore = KeyStore.getInstance("ESIGNER", provider);
Expand All @@ -64,4 +65,24 @@ public void testKeyStore() throws Exception {

assertNotNull("Signature", signature.sign());
}

@Test
public void testKeyStorePKCS11() throws Exception {
YubikeyTest.assumeYubikey();

JsignJcaProvider provider = new JsignJcaProvider();

KeyStore keystore = KeyStore.getInstance("YUBIKEY", provider);
keystore.load(null, "123456".toCharArray());
String alias = keystore.aliases().nextElement();

PrivateKey key = (PrivateKey) keystore.getKey(alias, null);
assertNotNull("key not found", key);

Signature signature = Signature.getInstance("SHA256withRSA", provider);
signature.initSign(key);
signature.update("Lorem ipsum dolor sit amet".getBytes());

assertNotNull("Signature", signature.sign());
}
}

0 comments on commit 94fcfd5

Please sign in to comment.