Hi, we are using JCAPI to sing and validate documents from an applet. It's the best proveder we have found at time, to access MSCAPI, so we'll gladly buy a license to use it, when all tests are OK.
Our problem: we sing and validate files, and everithings works fine when certificates and private keys are instaled in local machine.
When we use a certificate in an eToken to sign (production users will only use eToken, and will never install private key in local machine), nothing strange happens (it works, apparently). But when we validate the signature, an exception is thrown, due to an incorrect encripted digest (we use PKCS#7 to exchange signatures, generated and parsed with IAIK).
We have tested the same code using SunMSCAPI provider, from JDK 6, and it works well. Validation only fails when usin JCAPI provider to sign (no matter what provider is used to validate). But SunMSCAPI don't fits our needs, as well as JCAPI.
We have also tested eToken with an ActiveX we are actually using for the same thing (but we want to migrate to Java, to support more browsers), in the same environment, and it works.
So I think we can discard eToken and applet implementation as a cause.
I have make a small test program thas signs a simple text, using both JCAPI and SunMSCAPI, with same algorithm, certificate and eToken. And encrypted digests are different. Code follows:
import java.security.KeyStore;
import java.security.Security;
import java.security.Signature;
import java.security.UnrecoverableEntryException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import com.pheox.jcapi.JCAPIProperties;
import com.pheox.jcapi.JCAPIProvider;
import com.pheox.jcapi.JCAPIUtil;
public class TestJCAPI {
private final static String CN = "..."; // Put your ceritificate CN
private final static byte[] DATA = "Test".getBytes();
private static byte[] sign(KeyStore keyStore, String cn, byte[] data) throws Exception {
System.out.println("Using " + keyStore.getProvider().getName());
// Search entry by CN
KeyStore.PrivateKeyEntry privateKeyEntry = null;
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
X509Certificate x509Certificate = (X509Certificate) keyStore.getCertificate(alias);
if (x509Certificate.getSubjectX500Principal().getName().indexOf(cn) >= 0) {
try {
privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
} catch (UnrecoverableEntryException e) {
privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(null));
}
}
}
}
if (privateKeyEntry == null) {
System.out.println("Certificate not found");
return null;
}
// Sign
System.out.println("Signing with " + ((X509Certificate) privateKeyEntry.getCertificate()).getSubjectX500Principal());
Signature signature = Signature.getInstance("SHA1withRSA", keyStore.getProvider());
signature.initSign(privateKeyEntry.getPrivateKey());
signature.update(data);
byte[] encryptedDigest = signature.sign();
System.out.println("Encrypted Digest (" + encryptedDigest.length + "): " + Arrays.toString(encryptedDigest));
return encryptedDigest;
}
public static void main(String[] args) throws Exception {
Security.addProvider(new JCAPIProvider());
JCAPIProperties.setLogging(true);
System.out.println(JCAPIUtil.getEnvironmentInfo());
System.out.println();
KeyStore ksJcapi = KeyStore.getInstance("msks", "JCAPI");
ksJcapi.load(null, null);
JCAPIProperties.getInstance().setMSCertStoreNames(new String[]{"My"});
byte[] encryptedDigestJcapi = sign(ksJcapi, CN, DATA);
System.out.println();
KeyStore ksJdk = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
ksJdk.load(null, null);
byte[] encryptedDigestJdk = sign(ksJdk, CN, DATA);
System.out.println();
System.out.println(Arrays.equals(encryptedDigestJcapi, encryptedDigestJdk));
}
}
We don't have a clue of what's wrong.
Hardware is an USB eToken Pro 32K. Software is eToken Run Time Environment 3.65.