Проблемы создания PKCS#11
Я пытаюсь подписать PDF-файл, используя смарт-карту и PKCS#11. Я связываю правильную.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);
}
}
И это новая ошибка 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
Я осознаю, что это действительно глупо, помощь приветствуется.
2 ответа
У меня такая же проблема.
Попробуйте пройти -Djava.security.debug=sunpkcs11
в JVM. Я сделал это, и это сработало.
Если вы используете jarsigner
или же keytool
проходить -J-Djava.security.debug=sunpkcs11
вместо.
Смотрите ошибку OpenJDK. Эта проблема решена в OpenJDK, но, возможно, она все еще не решена в Oracle JDK.
Информировать slotListIndex
в конфиге это необязательно (однако метод getSlotsWithTokens()
не возвращает ожидаемое вами значение). Вы можете увидеть slotListIndex
описание параметра в PKCS11 Reference
:
Это индекс слота, с которым должен быть связан этот экземпляр провайдера. Это индекс в списке всех слотов, возвращаемых функцией PKCS#11 C_GetSlotList. Например, 0 обозначает первый слот в списке. Максимум один из слотов или 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);
Обратите внимание, что в качестве альтернативы очень часто используется обработчик обратного вызова пароля для доступа к хранилищам ключей 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);
KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);
ks.load(null, "yourPassword".toCharArray());