我正在尝试使用智能卡和PKCS#11签署pdf文件。我链接正确的.dll,我正在动态制作配置文件,但我遇到配置问题。
String config = "name=zz\n" +
"library=" + DLL + "\n" +
"slotListIndex = " + getSlotsWithTokens(DLL)[0];
ByteArrayInputStream pot = new ByteArrayInputStream(config.getBytes());
Provider providerPKCS11 = new SunPKCS11(pot);
我收到以下错误:
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
对整个插槽事情有点困惑。有人可以帮助我吗?
我的getSlotsWithTokens看起来如何:
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;
}
更新版本:
所以我按照@albciff建议进行了更改:这是完整的代码:
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);
}
}
这是新的错误消息:
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
我知道这是非常愚蠢的事情,欢迎帮助。
答案 0 :(得分:3)
我遇到了同样的问题。
尝试将-Djava.security.debug=sunpkcs11
传递给jvm。我做到了这一点并且有效。
如果您使用jarsigner
或keytool
传递-J-Djava.security.debug=sunpkcs11
,则
见OpenJDK bug。这个问题在OpenJDK中得到了解决,但在Oracle JDK中可能还没有解决。
答案 1 :(得分:2)
通知配置中的slotListIndex
它是可选的(但方法getSlotsWithTokens()
未返回您期望的值)。您可以在PKCS11 Reference
中看到slotListIndex
参数说明:
这是与此提供程序实例关联的插槽索引。它是PKCS#11函数C_GetSlotList返回的所有槽列表的索引。例如,0表示列表中的第一个插槽。最多可以指定slot或slotListIndex中的一个。 如果两者都未指定,则默认值为slotListIndex为0。
因此,仅配置name
和library
参数来配置您的PKCS11
提供商以避免您的例外:
// 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);
...
在您的代码中,您现在正确加载提供程序,因此java.security.KeyStoreException: PKCS11 not found
会在跟随调用KeyStore ks = KeyStore.getInstance("PKCS11");
中抛出,因为您的智能卡未插入或者是否插入了智能卡是提供程序的一些问题(两个问题抛出相同的异常),因此尝试使用getInstance(String,Provider)
将提供程序显式传递给实例:
使用:
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);
而不是:
KeyStore ks = KeyStore.getInstance("PKCS11");
此外,您的代码中存在错误:当您尝试加载密钥库时,必须提供 PKCS11 密码而不是null
。
所以使用:
ks.load(null, "yourPassword".toCharArray());
而不是:
ks.load(null, null);
注意或者使用password callback handler to access PKCS11 keystores非常常见。
总而言之,代码可以是:
// 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());