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