I am trying to sign a pdf file using the smart card and PKCS#11. I link the right .dll and I am making a configuration file dynamically, but I am running into configuration trouble.
String config = "name=zz\n" +
"library=" + DLL + "\n" +
"slotListIndex = " + getSlotsWithTokens(DLL)[0];
ByteArrayInputStream pot = new ByteArrayInputStream(config.getBytes());
Provider providerPKCS11 = new SunPKCS11(pot);
and I get the following error:
Exception in thread "main" java.security.ProviderException: Initialization failed
at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:376)
at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:107)
at smartCardPKCS11.scPKCS11.main(scPKCS11.java:56)
Caused by: java.security.ProviderException: slotListIndex is 52481 but token only has 10 slots
at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:357)
... 2 more
bit confused on the whole slots thing. Can someone help me out?
This how my getSlotsWithTokens looks:
public static long[] getSlotsWithTokens(String libraryPath) throws IOException{
CK_C_INITIALIZE_ARGS initArgs = new CK_C_INITIALIZE_ARGS();
String functionList = "C_GetFunctionList";
initArgs.flags = 0;
PKCS11 tmpPKCS11 = null;
long[] slotList = null;
try {
try {
tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, false);
} catch (IOException ex) {
ex.printStackTrace();
throw ex;
}
} catch (PKCS11Exception e) {
try {
initArgs = null;
tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, true);
} catch (IOException ex) {
ex.printStackTrace();
} catch (PKCS11Exception ex) {
ex.printStackTrace();
}
}
try {
slotList = tmpPKCS11.C_GetSlotList(true);
for (long slot : slotList){
CK_TOKEN_INFO tokenInfo = tmpPKCS11.C_GetTokenInfo(slot);
System.out.println("slot: "+slot+"\nmanufacturerID: "
+ String.valueOf(tokenInfo.manufacturerID) + "\nmodel: "
+ String.valueOf(tokenInfo.model));
}
} catch (PKCS11Exception ex) {
ex.printStackTrace();
} catch (Throwable t) {
t.printStackTrace();
}
return slotList;
}
UPDATED version:
So I made the changes as @albciff suggested: here is the complete code:
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS;
import sun.security.pkcs11.wrapper.CK_TOKEN_INFO;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Exception;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.log.LoggerFactory;
import com.itextpdf.text.log.SysoLogger;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CrlClient;
import com.itextpdf.text.pdf.security.CrlClientOnline;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.OcspClient;
import com.itextpdf.text.pdf.security.OcspClientBouncyCastle;
public class sPKCS11 {
public static final String SRC = "src/Test.pdf";
public static final String DEST = "src/scTest.pdf";
public static final String DLL = "c:/windows/system32/aetpkss1.dll";
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
LoggerFactory.getInstance().setLogger(new SysoLogger());
String pkcs11ConfigSettings = "name=aet\n"+"library="+DLL;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
Security.addProvider(pkcs11);
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, null);
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
System.out.println(aliases.nextElement());
}
// alias is here just for the sake of keeping things private
smartcardsign(pkcs11.getName(), ks, "alias");
}
public static void smartcardsign(String provider, KeyStore ks, String alias) throws GeneralSecurityException, IOException, DocumentException {
PrivateKey pk = (PrivateKey)ks.getKey(alias, null);
Certificate[] chain = ks.getCertificateChain(alias);
OcspClient ocspClient = new OcspClientBouncyCastle();
List<CrlClient> crlList = new ArrayList<CrlClient>();
crlList.add(new CrlClientOnline(chain));
scPKCS11 app = new scPKCS11();
app.sign(SRC, String.format(DEST, alias), chain, pk, DigestAlgorithms.SHA256, provider, CryptoStandard.CMS,
"Test", "B", crlList, ocspClient, null, 0);
}
public void sign(String src, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, CryptoStandard subfilter,
String reason, String location,
Collection<CrlClient> crlList,
OcspClient ocspClient,
TSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
}
And this is the new error msg:
Exception in thread "main" java.security.KeyStoreException: PKCS11 not found
at java.security.KeyStore.getInstance(Unknown Source)
at smartCardPKCS11.sPKCS11.main(sPKCS11.java:65)
Caused by: java.security.NoSuchAlgorithmException: PKCS11 KeyStore not available
at sun.security.jca.GetInstance.getInstance(Unknown Source)
at java.security.Security.getImpl(Unknown Source)
... 2 more
I am aware that it is something really stupid, help is welcomed.
The Public-Key Cryptography Standards (PKCS) comprise a group of cryptographic standards that provide guidelines and application programming interfaces (APIs) for the usage of cryptographic methods. As the name PKCS suggests, these standards put an emphasis on the usage of public key (that is, asymmetric) cryptography.
PKCS#12 (also known as PKCS12 or PFX) is a binary format for storing a certificate chain and private key in a single, encryptable file. PKCS#12 files are commonly used to import and export certificates and private keys on Windows and macOS computers, and usually have the filename extensions . p12 or .
PKCS12 (aka PFX) files, on the other hand, are language-neutral and is more secure and has been around long enough that it's supported just about everywhere.
Proxy Server supports Public Key Cryptography Standard (PKCS) #11, which defines the interface used for communication between SSL and PKCS #11 modules. PKCS #11 modules are used for standards-based connectivity to SSL hardware accelerators.
I had the same problem.
Try passing -Djava.security.debug=sunpkcs11
to jvm. I did this and it worked.
If you are using jarsigner
or keytool
pass -J-Djava.security.debug=sunpkcs11
instead.
See OpenJDK bug. This issue is solved in OpenJDK, but maybe it is still unresolved in Oracle JDK.
Inform the slotListIndex
in the config it's optional (however the method getSlotsWithTokens()
is not returning the value you expect). You can see the slotListIndex
parameter description in PKCS11 Reference
:
This is the slot index that this provider instance is to be associated with. It is the index into the list of all slots returned by the PKCS#11 function C_GetSlotList. For example, 0 indicates the first slot in the list. At most one of slot or slotListIndex may be specified. If neither is specified, the default is a slotListIndex of 0.
So config only name
and library
parameter to configure your PKCS11
provider to avoid your exception:
// Configure the Sun PKCS#11 provider. It requires a stream
// containing the configuration parameters - "name" and "library".
String pkcs11ConfigSettings = "name = " + pkcs11ID + "\n" + "library = " + libraryPath;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
// instantiate the provider
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
Security.addProvider(pkcs11);
...
In your code now you're loading the provider correctly so the java.security.KeyStoreException: PKCS11 not found
is thrown in the follow invocation KeyStore ks = KeyStore.getInstance("PKCS11");
because your smartcard is not plugged or if it's plugged maybe there is some problem with the provider (both problems thrown the same exception), so try to pass the provider explicitly to the instance using getInstance(String,Provider)
:
Use:
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);
Instead of:
KeyStore ks = KeyStore.getInstance("PKCS11");
Furthermore there is an error in your code: you have to provide the PKCS11 password not null
when you try to load the keystore.
So use:
ks.load(null, "yourPassword".toCharArray());
Instead of:
ks.load(null, null);
Note alternatively its very common to use a password callback handler to access PKCS11 keystores.
Putting all together, the code could be:
// Configure the Sun PKCS#11 provider. It requires a stream
// containing the configuration parameters - "name" and "library".
String pkcs11ConfigSettings = "name = " + pkcs11ID + "\n" + "library = " + libraryPath;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
// instantiate the provider
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
Security.addProvider(pkcs11);
KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);
ks.load(null, "yourPassword".toCharArray());
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