applet with SunMSCapi not working in linux

2019-04-15 11:00发布

问题:

We have created a new site for our web where we let the users to sign a pdf document using an applet we have designed. The issue is that this applet works fine only in Windows OS and we would like to extend it to linux OS.

When we run the applet in linux we get this error message:

[opensc-pkcs11] reader-pcsc.c:896:pcsc_detect_readers: SCardListReaders failed: 0x8010002e [opensc-pkcs11] reader-pcsc.c:1015:pcsc_detect_readers: returning with: No readers found [opensc-pkcs11] reader-pcsc.c:896:pcsc_detect_readers: SCardListReaders failed: 0x8010002e [opensc-pkcs11] reader-pcsc.c:1015:pcsc_detect_readers: returning with: No readers found java.security.NoSuchProviderException: no such provider: SunMSCAPI at sun.security.jca.GetInstance.getService(Unknown Source) at sun.security.jca.GetInstance.getInstance(Unknown Source)

I think the problem comes when we try to read the certificated stored in the Windows OS with this call in our code:

 KeyStore keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI"); 
            keystore.load(null, null); 
            return keystore;

This is te function we use to obtain the list of certificates.

public KeyStore  obtenerCertificados() throws Exception {

            String osNombre = System.getProperty("os.name");
            String osArquitectura = System.getProperty("os.arch");
            String providerConfig = null;
            String configuracionPKCS11 = null;

            // LINUX
            if(osNombre.contains(new StringBuffer("Linux")))
                providerConfig = "name = OpenSC\nlibrary = /usr/lib/opensc-pkcs11.so\n";
            // WINDOWS
            else if(osNombre.contains(new StringBuffer("Windows")))
                if(!osArquitectura.toLowerCase().contains("x86")){
                    System.out.println("Estamos en toLowerCase().contains x86");
                    providerConfig = "name = NSS"+"\n"+
                    "nssLibraryDirectory = "+"C:/Archivos de programa/Mozilla Firefox"+"\n"+
                    "nssSecmodDirectory = "+"C:/Users/SM/AppData/Local/Mozilla/Firefox/Profiles/plmk3eh9.default"+"\n"+
                    "nssDbMode = readOnly" + "\n" +
                    "nssModule = keystore" + "\n" + 
                    "\r";

                }
                else{
                    System.out.println("Estamos en NO toLowerCase().contains x86");
                    providerConfig = "name = NSS"+"\n"+
                    "nssLibraryDirectory = "+"C:/Program Files (x86)/Mozilla Firefox"+"\n"+
                    "nssLibrary = "+"C:/Program Files (x86)/Mozilla Firefox/softokn3.dll"+"\n"+
                    "nssSecmodDirectory = "+"C:/Users/SM/AppData/Roaming/Mozilla/Firefox/Profiles/plmk3eh9.default"+"\n"+
                    "nssDbMode = readOnly" + "\n" +
                    "nssModule = keystore" + "\n" + 
                    "\r";

                }
            // MAC OS
            else {providerConfig = "name = OpenSC\nlibrary = /Library/OpenSC/lib/opensc-pkcs11.so\n";}

            ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(providerConfig.getBytes());
            SunPKCS11 _pk11provider = null;
            try {
            _pk11provider = new SunPKCS11(localByteArrayInputStream);
            Security.addProvider(_pk11provider); 
//          _pk11provider.login(new Subject(), new DialogCallbackHandler());
            }catch(Throwable e){
            System.out.println(e.getMessage());
            }
            KeyStore keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI"); 
            keystore.load(null, null); 
            return keystore;


            }

Any ideas about how to extend this use to linux and MAC???

Thanks a lot for your help!!

回答1:

You can not use SunMSCAPI provider in Linux or MAC OS X, this provider is Windows specific which deals with Windows keystore. If you want to use a Smart Card or Firefox keystore in Linux or MAC OS X through SunPKCS11 provider you must get an instance of java.security.KeyStore passing SunPKCS11 as provider, like you are doing with SunMSCAPI i.e:

        ByteArrayInputStream confStream = ...// your configuration
        SunPKCS11 pkcs11 = new SunPKCS11(confStream);
        Security.addProvider(pkcs11);
        KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);
        ks.load(null, "your_pin".toCharArray());

With this code you load on the Keystore ks the keys from your configured PKCS11.

There is another way to do it if you want that your PKCS11 pin will be introduced by a third party later. To do so you can initialize your keystore with a java.security.KeyStore.CallbackHandlerProtection parameter like follows:

        ByteArrayInputStream confStream = ...// your configuration
        SunPKCS11 pkcs11 = new SunPKCS11(confStream);
        Security.addProvider(pkcs11);
        KeyStore.CallbackHandlerProtection cbhp = new KeyStore.CallbackHandlerProtection(new PinInputHandler(msg));
        KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", pkcs11, cbhp);
        builder.getKeyStore();

Note that PinInputHandler in this second sample must implements: javax.security.auth.callback.CallbackHandler.

Additionally in your code seems that you never loads the PKCS11 keys through SunPKCS11 provider (even in Windows case) because you are not instantiating a keystore with SunPKCS11 you are only adding it as a provider with Security.addProvider method and always you are instantiating only a keystore with SunMSCAPI, however probably in Windows you are getting some of your Smart cards keys because if you install Windows drivers for you smart card you can get their keys through Windows keystore.

Hope this helps,