Java - PKCS11 and MSKeyStore

2019-05-26 14:11发布

问题:

I'm trying to sign a string with different certs from MS-KeyStore. But, I know there are imported keys from a token in MS-Keystore. So, my problem is - if I go through the Keystore and try to sign with a cert which has a reference to pkcs11 I get a pop up to enter the pkcs11 password. How can I check if the cert is from my token?

Thanks in advance!!!

This is my code for now:

  String alias;
    byte[] data = "test".getBytes();
    char[] pin = "pass".toCharArray();

    try {


        KeyStore ks = KeyStore.getInstance("Windows-MY");
        ks.load(null, pin);
        System.out.println("Provider: "+ks.getProvider());
        System.out.println("KS size: " + ks.size());

        Enumeration enumeration = ks.aliases();

        while (enumeration.hasMoreElements()) {
            alias = (String) enumeration.nextElement();

            PrivateKey privateKey = (PrivateKey) ks.getKey(alias, null);
            Certificate certificate = ks.getCertificate(alias);

            Provider provider = ks.getProvider();
            Signature signature = Signature.getInstance("SHA1withRSA", provider);
            try {
                signature.initSign(privateKey);
                signature.update(data);

                byte[] signedSignature = signature.sign();
                System.out.println("\tGenerated signature for " + alias);

                signature.initVerify(certificate);
                signature.update(data);
                if (signature.verify(signedSignature)) {
                    System.out.println("\tSignature verifified for " + alias);
                } else {
                    System.out.println("\tCould not verify signature for " + alias);
                }
            } catch (Exception ex) {
                System.out.println("\tError for " + alias);
            }

        }

    } catch (KeyStoreException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    } catch (CertificateException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    } catch (FileNotFoundException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    } catch (IOException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    } catch (UnrecoverableKeyException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }

回答1:

I'm afraid you can't reliably tell the source of the certificate, at least not on the Java level for the MS CAPI provider. But that's part of the design - MS CAPI more or less intends to encapsulate and hide the origin of certificates/keys.

A safe way to tell that your key/certificate comes from a PKCS#11 device would be using the SUN PKCS#11 provider. This, however, has the disadvantage that you would need to specify the path to your native PKCS#11 library either statically (in the java.security file where you can statically configure providers) or dynamically request it as user input.

If using the PKCS#11 provider is too much trouble in your situation, I would suggest implementing a certificate choice dialog that filters for suitable certificates. There is no immediate gain in security by restricting MSCAPI to PKCS#11-originating certificates - there might be a good reason that your user has other certificates/keys installed (often in the form of PKCS#12 files). You should only check (and to help the user already filter certificates reagrding this criterion) that the certificate/key that was finally chosen meets your criteria: correct key usage (e.g. digital signature), sound extended key usage, acceptable or known policies present in the certificates etc.

In the EU we are slowly evolving towards the notion of "Qualified certificates on a secure signature creation device". This implies that certificates that are shipped on such a device (e.g. smart card) will contain a special policy, CAs are forbidden to use these policies for any other certificates, for example software certificates. So this would effectively allow you to ensure that a certificate originates from a secure hardware device. You might check if the certificates that are involved support this feature. This ETSI document lists the corresponding OIDs that you would have to look for.



回答2:

In Java key stores, the alias of the key and certificate should be linked. Basically, a private key entry is a private key + certificate chain. So the certificate should always have come from the key store. If the certificate came from the actual token is up to the implementation of the key store of course. The only way of checking if they were actually from the token is to retrieve them using a different method (e.g. reading the bytes of the certificate directly from the token). There is no link back to the storage device for certificates, if that is what you are after.

Of course, it does make sense to check the full chain of certificates up to the root certificate. If the root certificate does not change often, you might consider storing the certificate or a hash over the root certificate in a resource delivered with your application, or distributing it in the standard Java key store.