1 /*
2  * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.pkcs11;
27 
28 import java.io.*;
29 import java.util.*;
30 
31 import java.security.*;
32 import java.security.KeyStore.*;
33 import java.security.cert.X509Certificate;
34 
35 import sun.security.pkcs11.wrapper.*;
36 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
37 
38 
39 /**
40  * The Secmod class defines the interface to the native NSS
41  * library and the configuration information it stores in its
42  * secmod.db file.
43  *
44  * <p>Example code:
45  * <pre>
46  *   Secmod secmod = Secmod.getInstance();
47  *   if (secmod.isInitialized() == false) {
48  *       secmod.initialize("/home/myself/.mozilla", "/usr/sfw/lib/mozilla");
49  *   }
50  *
51  *   Provider p = secmod.getModule(ModuleType.KEYSTORE).getProvider();
52  *   KeyStore ks = KeyStore.getInstance("PKCS11", p);
53  *   ks.load(null, password);
54  * </pre>
55  *
56  * @since   1.6
57  * @author  Andreas Sterbenz
58  */
59 public final class Secmod {
60 
61     private final static boolean DEBUG = false;
62 
63     private final static Secmod INSTANCE;
64 
65     static {
sun.security.pkcs11.wrapper.PKCS11.loadNative()66         sun.security.pkcs11.wrapper.PKCS11.loadNative();
67         INSTANCE = new Secmod();
68     }
69 
70     private final static String NSS_LIB_NAME = "nss3";
71 
72     private final static String SOFTTOKEN_LIB_NAME = "softokn3";
73 
74     private final static String TRUST_LIB_NAME = "nssckbi";
75 
76     // Slot IDs - defined in j2secmod.h on the native side
77     // Values obtained from NSS's pkcs11i.h header
78 
79     private final static int NETSCAPE_SLOT_ID = 0x1;
80 
81     private final static int PRIVATE_KEY_SLOT_ID = 0x2;
82 
83     private final static int FIPS_SLOT_ID = 0x3;
84 
85     // handle to be passed to the native code, 0 means not initialized
86     private long nssHandle;
87 
88     // whether this is a supported version of NSS
89     private boolean supported;
90 
91     // list of the modules
92     private List<Module> modules;
93 
94     private String configDir;
95 
96     private String nssLibDir;
97 
Secmod()98     private Secmod() {
99         // empty
100     }
101 
102     /**
103      * Return the singleton Secmod instance.
104      */
getInstance()105     public static Secmod getInstance() {
106         return INSTANCE;
107     }
108 
isLoaded()109     private boolean isLoaded() {
110         if (nssHandle == 0) {
111             nssHandle = nssGetLibraryHandle(System.mapLibraryName(NSS_LIB_NAME));
112             if (nssHandle != 0) {
113                 fetchVersions();
114             }
115         }
116         return (nssHandle != 0);
117     }
118 
fetchVersions()119     private void fetchVersions() {
120         supported = nssVersionCheck(nssHandle, "3.7");
121     }
122 
123     /**
124      * Test whether this Secmod has been initialized. Returns true
125      * if NSS has been initialized using either the initialize() method
126      * or by directly calling the native NSS APIs. The latter may be
127      * the case if the current process contains components that use
128      * NSS directly.
129      *
130      * @throws IOException if an incompatible version of NSS
131      *   has been loaded
132      */
isInitialized()133     public synchronized boolean isInitialized() throws IOException {
134         // NSS does not allow us to check if it is initialized already
135         // assume that if it is loaded it is also initialized
136         if (isLoaded() == false) {
137             return false;
138         }
139         if (supported == false) {
140             throw new IOException
141                 ("An incompatible version of NSS is already loaded, "
142                 + "3.7 or later required");
143         }
144         return true;
145     }
146 
getConfigDir()147     String getConfigDir() {
148         return configDir;
149     }
150 
getLibDir()151     String getLibDir() {
152         return nssLibDir;
153     }
154 
155     /**
156      * Initialize this Secmod.
157      *
158      * @param configDir the directory containing the NSS configuration
159      *   files such as secmod.db
160      * @param nssLibDir the directory containing the NSS libraries
161      *   (libnss3.so or nss3.dll) or null if the library is on
162      *   the system default shared library path
163      *
164      * @throws IOException if NSS has already been initialized,
165      *   the specified directories are invalid, or initialization
166      *   fails for any other reason
167      */
initialize(String configDir, String nssLibDir)168     public void initialize(String configDir, String nssLibDir)
169             throws IOException {
170         initialize(DbMode.READ_WRITE, configDir, nssLibDir, false);
171     }
172 
initialize(DbMode dbMode, String configDir, String nssLibDir)173     public void initialize(DbMode dbMode, String configDir, String nssLibDir)
174             throws IOException {
175         initialize(dbMode, configDir, nssLibDir, false);
176     }
177 
initialize(DbMode dbMode, String configDir, String nssLibDir, boolean nssOptimizeSpace)178     public synchronized void initialize(DbMode dbMode, String configDir,
179         String nssLibDir, boolean nssOptimizeSpace) throws IOException {
180 
181         if (isInitialized()) {
182             throw new IOException("NSS is already initialized");
183         }
184 
185         if (dbMode == null) {
186             throw new NullPointerException();
187         }
188         if ((dbMode != DbMode.NO_DB) && (configDir == null)) {
189             throw new NullPointerException();
190         }
191         String platformLibName = System.mapLibraryName("nss3");
192         String platformPath;
193         if (nssLibDir == null) {
194             platformPath = platformLibName;
195         } else {
196             File base = new File(nssLibDir);
197             if (base.isDirectory() == false) {
198                 throw new IOException("nssLibDir must be a directory:" + nssLibDir);
199             }
200             File platformFile = new File(base, platformLibName);
201             if (platformFile.isFile() == false) {
202                 throw new FileNotFoundException(platformFile.getPath());
203             }
204             platformPath = platformFile.getPath();
205         }
206 
207         if (configDir != null) {
208             String configDirPath = null;
209             String sqlPrefix = "sql:/";
210             if (!configDir.startsWith(sqlPrefix)) {
211                 configDirPath = configDir;
212             } else {
213                 StringBuilder configDirPathSB = new StringBuilder(configDir);
214                 configDirPath = configDirPathSB.substring(sqlPrefix.length());
215             }
216             File configBase = new File(configDirPath);
217             if (configBase.isDirectory() == false ) {
218                 throw new IOException("configDir must be a directory: " + configDirPath);
219             }
220             if (!configDir.startsWith(sqlPrefix)) {
221                 File secmodFile = new File(configBase, "secmod.db");
222                 if (secmodFile.isFile() == false) {
223                     throw new FileNotFoundException(secmodFile.getPath());
224                 }
225             }
226         }
227 
228         if (DEBUG) System.out.println("lib: " + platformPath);
229         nssHandle = nssLoadLibrary(platformPath);
230         if (DEBUG) System.out.println("handle: " + nssHandle);
231         fetchVersions();
232         if (supported == false) {
233             throw new IOException
234                 ("The specified version of NSS is incompatible, "
235                 + "3.7 or later required");
236         }
237 
238         if (DEBUG) System.out.println("dir: " + configDir);
239         boolean initok = nssInitialize(dbMode.functionName, nssHandle,
240             configDir, nssOptimizeSpace);
241         if (DEBUG) System.out.println("init: " + initok);
242         if (initok == false) {
243             throw new IOException("NSS initialization failed");
244         }
245 
246         this.configDir = configDir;
247         this.nssLibDir = nssLibDir;
248     }
249 
250     /**
251      * Return an immutable list of all available modules.
252      *
253      * @throws IllegalStateException if this Secmod is misconfigured
254      *   or not initialized
255      */
getModules()256     public synchronized List<Module> getModules() {
257         try {
258             if (isInitialized() == false) {
259                 throw new IllegalStateException("NSS not initialized");
260             }
261         } catch (IOException e) {
262             // IOException if misconfigured
263             throw new IllegalStateException(e);
264         }
265         if (modules == null) {
266             @SuppressWarnings("unchecked")
267             List<Module> modules = (List<Module>)nssGetModuleList(nssHandle,
268                 nssLibDir);
269             this.modules = Collections.unmodifiableList(modules);
270         }
271         return modules;
272     }
273 
getDigest(X509Certificate cert, String algorithm)274     private static byte[] getDigest(X509Certificate cert, String algorithm) {
275         try {
276             MessageDigest md = MessageDigest.getInstance(algorithm);
277             return md.digest(cert.getEncoded());
278         } catch (GeneralSecurityException e) {
279             throw new ProviderException(e);
280         }
281     }
282 
isTrusted(X509Certificate cert, TrustType trustType)283     boolean isTrusted(X509Certificate cert, TrustType trustType) {
284         Bytes bytes = new Bytes(getDigest(cert, "SHA-1"));
285         TrustAttributes attr = getModuleTrust(ModuleType.KEYSTORE, bytes);
286         if (attr == null) {
287             attr = getModuleTrust(ModuleType.FIPS, bytes);
288             if (attr == null) {
289                 attr = getModuleTrust(ModuleType.TRUSTANCHOR, bytes);
290             }
291         }
292         return (attr == null) ? false : attr.isTrusted(trustType);
293     }
294 
getModuleTrust(ModuleType type, Bytes bytes)295     private TrustAttributes getModuleTrust(ModuleType type, Bytes bytes) {
296         Module module = getModule(type);
297         TrustAttributes t = (module == null) ? null : module.getTrust(bytes);
298         return t;
299     }
300 
301     /**
302      * Constants describing the different types of NSS modules.
303      * For this API, NSS modules are classified as either one
304      * of the internal modules delivered as part of NSS or
305      * as an external module provided by a 3rd party.
306      */
307     public static enum ModuleType {
308         /**
309          * The NSS Softtoken crypto module. This is the first
310          * slot of the softtoken object.
311          * This module provides
312          * implementations for cryptographic algorithms but no KeyStore.
313          */
314         CRYPTO,
315         /**
316          * The NSS Softtoken KeyStore module. This is the second
317          * slot of the softtoken object.
318          * This module provides
319          * implementations for cryptographic algorithms (after login)
320          * and the KeyStore.
321          */
322         KEYSTORE,
323         /**
324          * The NSS Softtoken module in FIPS mode. Note that in FIPS mode the
325          * softtoken presents only one slot, not separate CRYPTO and KEYSTORE
326          * slots as in non-FIPS mode.
327          */
328         FIPS,
329         /**
330          * The NSS builtin trust anchor module. This is the
331          * NSSCKBI object. It provides no crypto functions.
332          */
333         TRUSTANCHOR,
334         /**
335          * An external module.
336          */
337         EXTERNAL,
338     }
339 
340     /**
341      * Returns the first module of the specified type. If no such
342      * module exists, this method returns null.
343      *
344      * @throws IllegalStateException if this Secmod is misconfigured
345      *   or not initialized
346      */
getModule(ModuleType type)347     public Module getModule(ModuleType type) {
348         for (Module module : getModules()) {
349             if (module.getType() == type) {
350                 return module;
351             }
352         }
353         return null;
354     }
355 
356     static final String TEMPLATE_EXTERNAL =
357         "library = %s\n"
358         + "name = \"%s\"\n"
359         + "slotListIndex = %d\n";
360 
361     static final String TEMPLATE_TRUSTANCHOR =
362         "library = %s\n"
363         + "name = \"NSS Trust Anchors\"\n"
364         + "slotListIndex = 0\n"
365         + "enabledMechanisms = { KeyStore }\n"
366         + "nssUseSecmodTrust = true\n";
367 
368     static final String TEMPLATE_CRYPTO =
369         "library = %s\n"
370         + "name = \"NSS SoftToken Crypto\"\n"
371         + "slotListIndex = 0\n"
372         + "disabledMechanisms = { KeyStore }\n";
373 
374     static final String TEMPLATE_KEYSTORE =
375         "library = %s\n"
376         + "name = \"NSS SoftToken KeyStore\"\n"
377         + "slotListIndex = 1\n"
378         + "nssUseSecmodTrust = true\n";
379 
380     static final String TEMPLATE_FIPS =
381         "library = %s\n"
382         + "name = \"NSS FIPS SoftToken\"\n"
383         + "slotListIndex = 0\n"
384         + "nssUseSecmodTrust = true\n";
385 
386     /**
387      * A representation of one PKCS#11 slot in a PKCS#11 module.
388      */
389     public static final class Module {
390         // path of the native library
391         final String libraryName;
392         // descriptive name used by NSS
393         final String commonName;
394         final int slot;
395         final ModuleType type;
396 
397         private String config;
398         private SunPKCS11 provider;
399 
400         // trust attributes. Used for the KEYSTORE and TRUSTANCHOR modules only
401         private Map<Bytes,TrustAttributes> trust;
402 
Module(String libraryDir, String libraryName, String commonName, int slotIndex, int slotId)403         Module(String libraryDir, String libraryName, String commonName,
404                 int slotIndex, int slotId) {
405             ModuleType type;
406 
407             if ((libraryName == null) || (libraryName.length() == 0)) {
408                 // must be softtoken
409                 libraryName = System.mapLibraryName(SOFTTOKEN_LIB_NAME);
410                 if (slotId == NETSCAPE_SLOT_ID) {
411                     type = ModuleType.CRYPTO;
412                 } else if (slotId == PRIVATE_KEY_SLOT_ID) {
413                     type = ModuleType.KEYSTORE;
414                 } else if (slotId == FIPS_SLOT_ID) {
415                     type = ModuleType.FIPS;
416                 } else {
417                     throw new RuntimeException("Unexpected slot ID " + slotId +
418                             " in the NSS Internal Module");
419                 }
420             } else {
421                 if (libraryName.endsWith(System.mapLibraryName(TRUST_LIB_NAME))
422                         || commonName.equals("Builtin Roots Module")) {
423                     type = ModuleType.TRUSTANCHOR;
424                 } else {
425                     type = ModuleType.EXTERNAL;
426                 }
427             }
428             // On Ubuntu the libsoftokn3 library is located in a subdirectory
429             // of the system libraries directory. (Since Ubuntu 11.04.)
430             File libraryFile = new File(libraryDir, libraryName);
431             if (!libraryFile.isFile()) {
432                File failover = new File(libraryDir, "nss/" + libraryName);
433                if (failover.isFile()) {
434                    libraryFile = failover;
435                }
436             }
437             this.libraryName = libraryFile.getPath();
438             this.commonName = commonName;
439             this.slot = slotIndex;
440             this.type = type;
441             initConfiguration();
442         }
443 
initConfiguration()444         private void initConfiguration() {
445             switch (type) {
446             case EXTERNAL:
447                 config = String.format(TEMPLATE_EXTERNAL, libraryName,
448                                             commonName + " " + slot, slot);
449                 break;
450             case CRYPTO:
451                 config = String.format(TEMPLATE_CRYPTO, libraryName);
452                 break;
453             case KEYSTORE:
454                 config = String.format(TEMPLATE_KEYSTORE, libraryName);
455                 break;
456             case FIPS:
457                 config = String.format(TEMPLATE_FIPS, libraryName);
458                 break;
459             case TRUSTANCHOR:
460                 config = String.format(TEMPLATE_TRUSTANCHOR, libraryName);
461                 break;
462             default:
463                 throw new RuntimeException("Unknown module type: " + type);
464             }
465         }
466 
467         /**
468          * Get the configuration for this module. This is a string
469          * in the SunPKCS11 configuration format. It can be
470          * customized with additional options and then made
471          * current using the setConfiguration() method.
472          */
473         @Deprecated
getConfiguration()474         public synchronized String getConfiguration() {
475             return config;
476         }
477 
478         /**
479          * Set the configuration for this module.
480          *
481          * @throws IllegalStateException if the associated provider
482          *   instance has already been created.
483          */
484         @Deprecated
setConfiguration(String config)485         public synchronized void setConfiguration(String config) {
486             if (provider != null) {
487                 throw new IllegalStateException("Provider instance already created");
488             }
489             this.config = config;
490         }
491 
492         /**
493          * Return the pathname of the native library that implements
494          * this module. For example, /usr/lib/libpkcs11.so.
495          */
getLibraryName()496         public String getLibraryName() {
497             return libraryName;
498         }
499 
500         /**
501          * Returns the type of this module.
502          */
getType()503         public ModuleType getType() {
504             return type;
505         }
506 
507         /**
508          * Returns the provider instance that is associated with this
509          * module. The first call to this method creates the provider
510          * instance.
511          */
512         @Deprecated
getProvider()513         public synchronized Provider getProvider() {
514             if (provider == null) {
515                 provider = newProvider();
516             }
517             return provider;
518         }
519 
hasInitializedProvider()520         synchronized boolean hasInitializedProvider() {
521             return provider != null;
522         }
523 
setProvider(SunPKCS11 p)524         void setProvider(SunPKCS11 p) {
525             if (provider != null) {
526                 throw new ProviderException("Secmod provider already initialized");
527             }
528             provider = p;
529         }
530 
newProvider()531         private SunPKCS11 newProvider() {
532             try {
533                 InputStream in = new ByteArrayInputStream(config.getBytes("UTF8"));
534                 return new SunPKCS11(in);
535             } catch (Exception e) {
536                 // XXX
537                 throw new ProviderException(e);
538             }
539         }
540 
setTrust(Token token, X509Certificate cert)541         synchronized void setTrust(Token token, X509Certificate cert) {
542             Bytes bytes = new Bytes(getDigest(cert, "SHA-1"));
543             TrustAttributes attr = getTrust(bytes);
544             if (attr == null) {
545                 attr = new TrustAttributes(token, cert, bytes, CKT_NETSCAPE_TRUSTED_DELEGATOR);
546                 trust.put(bytes, attr);
547             } else {
548                 // does it already have the correct trust settings?
549                 if (attr.isTrusted(TrustType.ALL) == false) {
550                     // XXX not yet implemented
551                     throw new ProviderException("Cannot change existing trust attributes");
552                 }
553             }
554         }
555 
getTrust(Bytes hash)556         TrustAttributes getTrust(Bytes hash) {
557             if (trust == null) {
558                 // If provider is not set, create a temporary provider to
559                 // retrieve the trust information. This can happen if we need
560                 // to get the trust information for the trustanchor module
561                 // because we need to look for user customized settings in the
562                 // keystore module (which may not have a provider created yet).
563                 // Creating a temporary provider and then dropping it on the
564                 // floor immediately is flawed, but it's the best we can do
565                 // for now.
566                 synchronized (this) {
567                     SunPKCS11 p = provider;
568                     if (p == null) {
569                         p = newProvider();
570                     }
571                     try {
572                         trust = Secmod.getTrust(p);
573                     } catch (PKCS11Exception e) {
574                         throw new RuntimeException(e);
575                     }
576                 }
577             }
578             return trust.get(hash);
579         }
580 
toString()581         public String toString() {
582             return
583             commonName + " (" + type + ", " + libraryName + ", slot " + slot + ")";
584         }
585 
586     }
587 
588     /**
589      * Constants representing NSS trust categories.
590      */
591     public static enum TrustType {
592         /** Trusted for all purposes */
593         ALL,
594         /** Trusted for SSL client authentication */
595         CLIENT_AUTH,
596         /** Trusted for SSL server authentication */
597         SERVER_AUTH,
598         /** Trusted for code signing */
599         CODE_SIGNING,
600         /** Trusted for email protection */
601         EMAIL_PROTECTION,
602     }
603 
604     public static enum DbMode {
605         READ_WRITE("NSS_InitReadWrite"),
606         READ_ONLY ("NSS_Init"),
607         NO_DB     ("NSS_NoDB_Init");
608 
609         final String functionName;
DbMode(String functionName)610         DbMode(String functionName) {
611             this.functionName = functionName;
612         }
613     }
614 
615     /**
616      * A LoadStoreParameter for use with the NSS Softtoken or
617      * NSS TrustAnchor KeyStores.
618      * <p>
619      * It allows the set of trusted certificates that are returned by
620      * the KeyStore to be specified.
621      */
622     public static final class KeyStoreLoadParameter implements LoadStoreParameter {
623         final TrustType trustType;
624         final ProtectionParameter protection;
KeyStoreLoadParameter(TrustType trustType, char[] password)625         public KeyStoreLoadParameter(TrustType trustType, char[] password) {
626             this(trustType, new PasswordProtection(password));
627 
628         }
KeyStoreLoadParameter(TrustType trustType, ProtectionParameter prot)629         public KeyStoreLoadParameter(TrustType trustType, ProtectionParameter prot) {
630             if (trustType == null) {
631                 throw new NullPointerException("trustType must not be null");
632             }
633             this.trustType = trustType;
634             this.protection = prot;
635         }
getProtectionParameter()636         public ProtectionParameter getProtectionParameter() {
637             return protection;
638         }
getTrustType()639         public TrustType getTrustType() {
640             return trustType;
641         }
642     }
643 
644     static class TrustAttributes {
645         final long handle;
646         final long clientAuth, serverAuth, codeSigning, emailProtection;
647         final byte[] shaHash;
TrustAttributes(Token token, X509Certificate cert, Bytes bytes, long trustValue)648         TrustAttributes(Token token, X509Certificate cert, Bytes bytes, long trustValue) {
649             Session session = null;
650             try {
651                 session = token.getOpSession();
652                 // XXX use KeyStore TrustType settings to determine which
653                 // attributes to set
654                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
655                     new CK_ATTRIBUTE(CKA_TOKEN, true),
656                     new CK_ATTRIBUTE(CKA_CLASS, CKO_NETSCAPE_TRUST),
657                     new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_SERVER_AUTH, trustValue),
658                     new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CODE_SIGNING, trustValue),
659                     new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_EMAIL_PROTECTION, trustValue),
660                     new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CLIENT_AUTH, trustValue),
661                     new CK_ATTRIBUTE(CKA_NETSCAPE_CERT_SHA1_HASH, bytes.b),
662                     new CK_ATTRIBUTE(CKA_NETSCAPE_CERT_MD5_HASH, getDigest(cert, "MD5")),
663                     new CK_ATTRIBUTE(CKA_ISSUER, cert.getIssuerX500Principal().getEncoded()),
664                     new CK_ATTRIBUTE(CKA_SERIAL_NUMBER, cert.getSerialNumber().toByteArray()),
665                     // XXX per PKCS#11 spec, the serial number should be in ASN.1
666                 };
667                 handle = token.p11.C_CreateObject(session.id(), attrs);
668                 shaHash = bytes.b;
669                 clientAuth = trustValue;
670                 serverAuth = trustValue;
671                 codeSigning = trustValue;
672                 emailProtection = trustValue;
673             } catch (PKCS11Exception e) {
674                 throw new ProviderException("Could not create trust object", e);
675             } finally {
676                 token.releaseSession(session);
677             }
678         }
TrustAttributes(Token token, Session session, long handle)679         TrustAttributes(Token token, Session session, long handle)
680                         throws PKCS11Exception {
681             this.handle = handle;
682             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
683                 new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_SERVER_AUTH),
684                 new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CODE_SIGNING),
685                 new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_EMAIL_PROTECTION),
686                 new CK_ATTRIBUTE(CKA_NETSCAPE_CERT_SHA1_HASH),
687             };
688 
689             token.p11.C_GetAttributeValue(session.id(), handle, attrs);
690             serverAuth = attrs[0].getLong();
691             codeSigning = attrs[1].getLong();
692             emailProtection = attrs[2].getLong();
693             shaHash = attrs[3].getByteArray();
694 
695             attrs = new CK_ATTRIBUTE[] {
696                 new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CLIENT_AUTH),
697             };
698             long c;
699             try {
700                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
701                 c = attrs[0].getLong();
702             } catch (PKCS11Exception e) {
703                 // trust anchor module does not support this attribute
704                 c = serverAuth;
705             }
706             clientAuth = c;
707         }
getHash()708         Bytes getHash() {
709             return new Bytes(shaHash);
710         }
isTrusted(TrustType type)711         boolean isTrusted(TrustType type) {
712             switch (type) {
713             case CLIENT_AUTH:
714                 return isTrusted(clientAuth);
715             case SERVER_AUTH:
716                 return isTrusted(serverAuth);
717             case CODE_SIGNING:
718                 return isTrusted(codeSigning);
719             case EMAIL_PROTECTION:
720                 return isTrusted(emailProtection);
721             case ALL:
722                 return isTrusted(TrustType.CLIENT_AUTH)
723                     && isTrusted(TrustType.SERVER_AUTH)
724                     && isTrusted(TrustType.CODE_SIGNING)
725                     && isTrusted(TrustType.EMAIL_PROTECTION);
726             default:
727                 return false;
728             }
729         }
730 
isTrusted(long l)731         private boolean isTrusted(long l) {
732             // XXX CKT_TRUSTED?
733             return (l == CKT_NETSCAPE_TRUSTED_DELEGATOR);
734         }
735 
736     }
737 
738     private static class Bytes {
739         final byte[] b;
Bytes(byte[] b)740         Bytes(byte[] b) {
741             this.b = b;
742         }
hashCode()743         public int hashCode() {
744             return Arrays.hashCode(b);
745         }
equals(Object o)746         public boolean equals(Object o) {
747             if (this == o) {
748                 return true;
749             }
750             if (o instanceof Bytes == false) {
751                 return false;
752             }
753             Bytes other = (Bytes)o;
754             return Arrays.equals(this.b, other.b);
755         }
756     }
757 
getTrust(SunPKCS11 provider)758     private static Map<Bytes,TrustAttributes> getTrust(SunPKCS11 provider)
759             throws PKCS11Exception {
760         Map<Bytes,TrustAttributes> trustMap = new HashMap<Bytes,TrustAttributes>();
761         Token token = provider.getToken();
762         Session session = null;
763         try {
764             session = token.getOpSession();
765             int MAX_NUM = 8192;
766             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
767                 new CK_ATTRIBUTE(CKA_CLASS, CKO_NETSCAPE_TRUST),
768             };
769             token.p11.C_FindObjectsInit(session.id(), attrs);
770             long[] handles = token.p11.C_FindObjects(session.id(), MAX_NUM);
771             token.p11.C_FindObjectsFinal(session.id());
772             if (DEBUG) System.out.println("handles: " + handles.length);
773 
774             for (long handle : handles) {
775                 try {
776                     TrustAttributes trust = new TrustAttributes(token, session, handle);
777                     trustMap.put(trust.getHash(), trust);
778                 } catch (PKCS11Exception e) {
779                     // skip put on pkcs11 error
780                 }
781             }
782         } finally {
783             token.releaseSession(session);
784         }
785         return trustMap;
786     }
787 
nssGetLibraryHandle(String libraryName)788     private static native long nssGetLibraryHandle(String libraryName);
789 
nssLoadLibrary(String name)790     private static native long nssLoadLibrary(String name) throws IOException;
791 
nssVersionCheck(long handle, String minVersion)792     private static native boolean nssVersionCheck(long handle, String minVersion);
793 
nssInitialize(String functionName, long handle, String configDir, boolean nssOptimizeSpace)794     private static native boolean nssInitialize(String functionName, long handle, String configDir, boolean nssOptimizeSpace);
795 
nssGetModuleList(long handle, String libDir)796     private static native Object nssGetModuleList(long handle, String libDir);
797 
798 }
799