예제 #1
0
  /**
   * Assigns the given key (that has already been protected) to the given alias.
   *
   * <p>If the protected key is of type <code>java.security.PrivateKey</code>, it must be
   * accompanied by a certificate chain certifying the corresponding public key. If the underlying
   * keystore implementation is of type <code>jks</code>, <code>key</code> must be encoded as an
   * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
   *
   * <p>If the given alias already exists, the keystore information associated with it is overridden
   * by the given key (and possibly certificate chain).
   *
   * @param alias the alias name
   * @param key the key (in protected format) to be associated with the alias
   * @param chain the certificate chain for the corresponding public key (only useful if the
   *     protected key is of type <code>java.security.PrivateKey</code>).
   * @exception KeyStoreException if this operation fails.
   */
  public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
      throws KeyStoreException {
    permissionCheck();

    synchronized (entries) {
      // key must be encoded as EncryptedPrivateKeyInfo as defined in
      // PKCS#8
      KeyEntry entry = new KeyEntry();
      try {
        EncryptedPrivateKeyInfo privateKey = new EncryptedPrivateKeyInfo(key);
        entry.protectedPrivKey = privateKey.getEncoded();
      } catch (IOException ioe) {
        throw new KeyStoreException("key is not encoded as " + "EncryptedPrivateKeyInfo");
      }

      entry.date = new Date();

      if ((chain != null) && (chain.length != 0)) {
        entry.chain = chain.clone();
        entry.chainRefs = new long[entry.chain.length];
      }

      String lowerAlias = alias.toLowerCase();
      if (entries.get(lowerAlias) != null) {
        deletedEntries.put(lowerAlias, entries.get(alias));
      }
      entries.put(lowerAlias, entry);
      addedEntries.put(lowerAlias, entry);
    }
  }
예제 #2
0
  /**
   * Loads the keystore from the Keychain.
   *
   * @param stream Ignored - here for API compatibility.
   * @param password Ignored - if user needs to unlock keychain Security framework will post any
   *     dialogs.
   * @exception IOException if there is an I/O or format problem with the keystore data
   * @exception NoSuchAlgorithmException if the algorithm used to check the integrity of the
   *     keystore cannot be found
   * @exception CertificateException if any of the certificates in the keystore could not be loaded
   */
  public void engineLoad(InputStream stream, char[] password)
      throws IOException, NoSuchAlgorithmException, CertificateException {
    permissionCheck();

    // Release any stray keychain references before clearing out the entries.
    synchronized (entries) {
      for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
        String alias = e.nextElement();
        Object entry = entries.get(alias);
        if (entry instanceof TrustedCertEntry) {
          if (((TrustedCertEntry) entry).certRef != 0) {
            _releaseKeychainItemRef(((TrustedCertEntry) entry).certRef);
          }
        } else {
          KeyEntry keyEntry = (KeyEntry) entry;

          if (keyEntry.chain != null) {
            for (int i = 0; i < keyEntry.chain.length; i++) {
              if (keyEntry.chainRefs[i] != 0) {
                _releaseKeychainItemRef(keyEntry.chainRefs[i]);
              }
            }

            if (keyEntry.keyRef != 0) {
              _releaseKeychainItemRef(keyEntry.keyRef);
            }
          }
        }
      }

      entries.clear();
      _scanKeychain();
    }
  }
예제 #3
0
  /**
   * Callback method from _scanKeychain. If a trusted certificate is found, this method will be
   * called.
   */
  private void createTrustedCertEntry(
      String alias, long keychainItemRef, long creationDate, byte[] derStream) {
    TrustedCertEntry tce = new TrustedCertEntry();

    try {
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      InputStream input = new ByteArrayInputStream(derStream);
      X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
      input.close();
      tce.cert = cert;
      tce.certRef = keychainItemRef;

      // Make a creation date.
      if (creationDate != 0) tce.date = new Date(creationDate);
      else tce.date = new Date();

      int uniqueVal = 1;
      String originalAlias = alias;

      while (entries.containsKey(alias.toLowerCase())) {
        alias = originalAlias + " " + uniqueVal;
        uniqueVal++;
      }

      entries.put(alias.toLowerCase(), tce);
    } catch (Exception e) {
      // The certificate will be skipped.
      System.err.println("KeychainStore Ignored Exception: " + e);
    }
  }
예제 #4
0
  /**
   * Deletes the entry identified by the given alias from this keystore.
   *
   * @param alias the alias name
   * @exception KeyStoreException if the entry cannot be removed.
   */
  public void engineDeleteEntry(String alias) throws KeyStoreException {
    permissionCheck();

    synchronized (entries) {
      Object entry = entries.remove(alias.toLowerCase());
      deletedEntries.put(alias.toLowerCase(), entry);
    }
  }
예제 #5
0
  /**
   * Assigns the given certificate to the given alias.
   *
   * <p>If the given alias already exists in this keystore and identifies a <i>trusted certificate
   * entry</i>, the certificate associated with it is overridden by the given certificate.
   *
   * @param alias the alias name
   * @param cert the certificate
   * @exception KeyStoreException if the given alias already exists and does not identify a
   *     <i>trusted certificate entry</i>, or this operation fails for some other reason.
   */
  public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
    permissionCheck();

    synchronized (entries) {
      Object entry = entries.get(alias.toLowerCase());
      if ((entry != null) && (entry instanceof KeyEntry)) {
        throw new KeyStoreException("Cannot overwrite key entry with certificate");
      }

      // This will be slow, but necessary.  Enumerate the values and then see if the cert matches
      // the one in the trusted cert entry.
      // Security framework doesn't support the same certificate twice in a keychain.
      Collection<Object> allValues = entries.values();

      for (Object value : allValues) {
        if (value instanceof TrustedCertEntry) {
          TrustedCertEntry tce = (TrustedCertEntry) value;
          if (tce.cert.equals(cert)) {
            throw new KeyStoreException(
                "Keychain does not support mulitple copies of same certificate.");
          }
        }
      }

      TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
      trustedCertEntry.cert = cert;
      trustedCertEntry.date = new Date();
      String lowerAlias = alias.toLowerCase();
      if (entries.get(lowerAlias) != null) {
        deletedEntries.put(lowerAlias, entries.get(lowerAlias));
      }
      entries.put(lowerAlias, trustedCertEntry);
      addedEntries.put(lowerAlias, trustedCertEntry);
    }
  }
예제 #6
0
 /**
  * Returns true if the entry identified by the given alias is a <i>trusted certificate entry</i>,
  * and false otherwise.
  *
  * @return true if the entry identified by the given alias is a <i>trusted certificate entry</i>,
  *     false otherwise.
  */
 public boolean engineIsCertificateEntry(String alias) {
   permissionCheck();
   Object entry = entries.get(alias.toLowerCase());
   if ((entry != null) && (entry instanceof TrustedCertEntry)) {
     return true;
   } else {
     return false;
   }
 }
예제 #7
0
  /**
   * Assigns the given key to the given alias, protecting it with the given password.
   *
   * <p>If the given key is of type <code>java.security.PrivateKey</code>, it must be accompanied by
   * a certificate chain certifying the corresponding public key.
   *
   * <p>If the given alias already exists, the keystore information associated with it is overridden
   * by the given key (and possibly certificate chain).
   *
   * @param alias the alias name
   * @param key the key to be associated with the alias
   * @param password the password to protect the key
   * @param chain the certificate chain for the corresponding public key (only required if the given
   *     key is of type <code>java.security.PrivateKey</code>).
   * @exception KeyStoreException if the given key cannot be protected, or this operation fails for
   *     some other reason
   */
  public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
      throws KeyStoreException {
    permissionCheck();

    synchronized (entries) {
      try {
        KeyEntry entry = new KeyEntry();
        entry.date = new Date();

        if (key instanceof PrivateKey) {
          if ((key.getFormat().equals("PKCS#8")) || (key.getFormat().equals("PKCS8"))) {
            entry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), password);
            entry.password = password.clone();
          } else {
            throw new KeyStoreException("Private key is not encoded as PKCS#8");
          }
        } else {
          throw new KeyStoreException("Key is not a PrivateKey");
        }

        // clone the chain
        if (chain != null) {
          if ((chain.length > 1) && !validateChain(chain)) {
            throw new KeyStoreException("Certificate chain does not validate");
          }

          entry.chain = chain.clone();
          entry.chainRefs = new long[entry.chain.length];
        }

        String lowerAlias = alias.toLowerCase();
        if (entries.get(lowerAlias) != null) {
          deletedEntries.put(lowerAlias, entries.get(lowerAlias));
        }

        entries.put(lowerAlias, entry);
        addedEntries.put(lowerAlias, entry);
      } catch (Exception nsae) {
        KeyStoreException ke = new KeyStoreException("Key protection algorithm not found: " + nsae);
        ke.initCause(nsae);
        throw ke;
      }
    }
  }
예제 #8
0
  /**
   * Returns the (alias) name of the first keystore entry whose certificate matches the given
   * certificate.
   *
   * <p>This method attempts to match the given certificate with each keystore entry. If the entry
   * being considered is a <i>trusted certificate entry</i>, the given certificate is compared to
   * that entry's certificate. If the entry being considered is a <i>key entry</i>, the given
   * certificate is compared to the first element of that entry's certificate chain (if a chain
   * exists).
   *
   * @param cert the certificate to match with.
   * @return the (alias) name of the first entry with matching certificate, or null if no such entry
   *     exists in this keystore.
   */
  public String engineGetCertificateAlias(Certificate cert) {
    permissionCheck();
    Certificate certElem;

    for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
      String alias = e.nextElement();
      Object entry = entries.get(alias);
      if (entry instanceof TrustedCertEntry) {
        certElem = ((TrustedCertEntry) entry).cert;
      } else {
        KeyEntry ke = (KeyEntry) entry;
        if (ke.chain == null || ke.chain.length == 0) {
          continue;
        }
        certElem = ke.chain[0];
      }
      if (certElem.equals(cert)) {
        return alias;
      }
    }
    return null;
  }
예제 #9
0
  /**
   * Returns the certificate chain associated with the given alias.
   *
   * @param alias the alias name
   * @return the certificate chain (ordered with the user's certificate first and the root
   *     certificate authority last), or null if the given alias does not exist or does not contain
   *     a certificate chain (i.e., the given alias identifies either a <i>trusted certificate
   *     entry</i> or a <i>key entry</i> without a certificate chain).
   */
  public Certificate[] engineGetCertificateChain(String alias) {
    permissionCheck();

    Object entry = entries.get(alias.toLowerCase());

    if (entry != null && entry instanceof KeyEntry) {
      if (((KeyEntry) entry).chain == null) {
        return null;
      } else {
        return ((KeyEntry) entry).chain.clone();
      }
    } else {
      return null;
    }
  }
예제 #10
0
  /**
   * Returns the creation date of the entry identified by the given alias.
   *
   * @param alias the alias name
   * @return the creation date of this entry, or null if the given alias does not exist
   */
  public Date engineGetCreationDate(String alias) {
    permissionCheck();

    Object entry = entries.get(alias.toLowerCase());

    if (entry != null) {
      if (entry instanceof TrustedCertEntry) {
        return new Date(((TrustedCertEntry) entry).date.getTime());
      } else {
        return new Date(((KeyEntry) entry).date.getTime());
      }
    } else {
      return null;
    }
  }
예제 #11
0
  /**
   * Returns the certificate associated with the given alias.
   *
   * <p>If the given alias name identifies a <i>trusted certificate entry</i>, the certificate
   * associated with that entry is returned. If the given alias name identifies a <i>key entry</i>,
   * the first element of the certificate chain of that entry is returned, or null if that entry
   * does not have a certificate chain.
   *
   * @param alias the alias name
   * @return the certificate, or null if the given alias does not exist or does not contain a
   *     certificate.
   */
  public Certificate engineGetCertificate(String alias) {
    permissionCheck();

    Object entry = entries.get(alias.toLowerCase());

    if (entry != null) {
      if (entry instanceof TrustedCertEntry) {
        return ((TrustedCertEntry) entry).cert;
      } else {
        KeyEntry ke = (KeyEntry) entry;
        if (ke.chain == null || ke.chain.length == 0) {
          return null;
        }
        return ke.chain[0];
      }
    } else {
      return null;
    }
  }
예제 #12
0
  /**
   * Stores this keystore to the given output stream, and protects its integrity with the given
   * password.
   *
   * @param stream Ignored. the output stream to which this keystore is written.
   * @param password the password to generate the keystore integrity check
   * @exception IOException if there was an I/O problem with data
   * @exception NoSuchAlgorithmException if the appropriate data integrity algorithm could not be
   *     found
   * @exception CertificateException if any of the certificates included in the keystore data could
   *     not be stored
   */
  public void engineStore(OutputStream stream, char[] password)
      throws IOException, NoSuchAlgorithmException, CertificateException {
    permissionCheck();

    // Delete items that do have a keychain item ref.
    for (Enumeration<String> e = deletedEntries.keys(); e.hasMoreElements(); ) {
      String alias = e.nextElement();
      Object entry = deletedEntries.get(alias);
      if (entry instanceof TrustedCertEntry) {
        if (((TrustedCertEntry) entry).certRef != 0) {
          _removeItemFromKeychain(((TrustedCertEntry) entry).certRef);
          _releaseKeychainItemRef(((TrustedCertEntry) entry).certRef);
        }
      } else {
        Certificate certElem;
        KeyEntry keyEntry = (KeyEntry) entry;

        if (keyEntry.chain != null) {
          for (int i = 0; i < keyEntry.chain.length; i++) {
            if (keyEntry.chainRefs[i] != 0) {
              _removeItemFromKeychain(keyEntry.chainRefs[i]);
              _releaseKeychainItemRef(keyEntry.chainRefs[i]);
            }
          }

          if (keyEntry.keyRef != 0) {
            _removeItemFromKeychain(keyEntry.keyRef);
            _releaseKeychainItemRef(keyEntry.keyRef);
          }
        }
      }
    }

    // Add all of the certs or keys in the added entries.
    // No need to check for 0 refs, as they are in the added list.
    for (Enumeration<String> e = addedEntries.keys(); e.hasMoreElements(); ) {
      String alias = e.nextElement();
      Object entry = addedEntries.get(alias);
      if (entry instanceof TrustedCertEntry) {
        TrustedCertEntry tce = (TrustedCertEntry) entry;
        Certificate certElem;
        certElem = tce.cert;
        tce.certRef = addCertificateToKeychain(alias, certElem);
      } else {
        KeyEntry keyEntry = (KeyEntry) entry;

        if (keyEntry.chain != null) {
          for (int i = 0; i < keyEntry.chain.length; i++) {
            keyEntry.chainRefs[i] = addCertificateToKeychain(alias, keyEntry.chain[i]);
          }

          keyEntry.keyRef =
              _addItemToKeychain(alias, false, keyEntry.protectedPrivKey, keyEntry.password);
        }
      }
    }

    // Clear the added and deletedEntries hashtables here, now that we're done with the updates.
    // For the deleted entries, we freed up the native references above.
    deletedEntries.clear();
    addedEntries.clear();
  }
예제 #13
0
 /**
  * Retrieves the number of entries in this keystore.
  *
  * @return the number of entries in this keystore
  */
 public int engineSize() {
   permissionCheck();
   return entries.size();
 }
예제 #14
0
 /**
  * Checks if the given alias exists in this keystore.
  *
  * @param alias the alias name
  * @return true if the alias exists, false otherwise
  */
 public boolean engineContainsAlias(String alias) {
   permissionCheck();
   return entries.containsKey(alias.toLowerCase());
 }
예제 #15
0
 /**
  * Lists all the alias names of this keystore.
  *
  * @return enumeration of the alias names
  */
 public Enumeration<String> engineAliases() {
   permissionCheck();
   return entries.keys();
 }
예제 #16
0
  /**
   * Callback method from _scanKeychain. If an identity is found, this method will be called to
   * create Java certificate and private key objects from the keychain data.
   */
  private void createKeyEntry(
      String alias,
      long creationDate,
      long secKeyRef,
      long[] secCertificateRefs,
      byte[][] rawCertData)
      throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
    KeyEntry ke = new KeyEntry();

    // First, store off the private key information.  This is the easy part.
    ke.protectedPrivKey = null;
    ke.keyRef = secKeyRef;

    // Make a creation date.
    if (creationDate != 0) ke.date = new Date(creationDate);
    else ke.date = new Date();

    // Next, create X.509 Certificate objects from the raw data.  This is complicated
    // because a certificate's public key may be too long for Java's default encryption strength.
    List<CertKeychainItemPair> createdCerts = new ArrayList<>();

    try {
      CertificateFactory cf = CertificateFactory.getInstance("X.509");

      for (int i = 0; i < rawCertData.length; i++) {
        try {
          InputStream input = new ByteArrayInputStream(rawCertData[i]);
          X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
          input.close();

          // We successfully created the certificate, so track it and its corresponding
          // SecCertificateRef.
          createdCerts.add(new CertKeychainItemPair(secCertificateRefs[i], cert));
        } catch (CertificateException e) {
          // The certificate will be skipped.
          System.err.println("KeychainStore Ignored Exception: " + e);
        }
      }
    } catch (CertificateException e) {
      e.printStackTrace();
    } catch (IOException ioe) {
      ioe.printStackTrace(); // How would this happen?
    }

    // We have our certificates in the List, so now extract them into an array of
    // Certificates and SecCertificateRefs.
    CertKeychainItemPair[] objArray = createdCerts.toArray(new CertKeychainItemPair[0]);
    Certificate[] certArray = new Certificate[objArray.length];
    long[] certRefArray = new long[objArray.length];

    for (int i = 0; i < objArray.length; i++) {
      CertKeychainItemPair addedItem = objArray[i];
      certArray[i] = addedItem.mCert;
      certRefArray[i] = addedItem.mCertificateRef;
    }

    ke.chain = certArray;
    ke.chainRefs = certRefArray;

    // If we don't have already have an item with this item's alias
    // create a new one for it.
    int uniqueVal = 1;
    String originalAlias = alias;

    while (entries.containsKey(alias.toLowerCase())) {
      alias = originalAlias + " " + uniqueVal;
      uniqueVal++;
    }

    entries.put(alias.toLowerCase(), ke);
  }
예제 #17
0
  /**
   * Returns the key associated with the given alias, using the given password to recover it.
   *
   * @param alias the alias name
   * @param password the password for recovering the key. This password is used internally as the
   *     key is exported in a PKCS12 format.
   * @return the requested key, or null if the given alias does not exist or does not identify a
   *     <i>key entry</i>.
   * @exception NoSuchAlgorithmException if the algorithm for recovering the key cannot be found
   * @exception UnrecoverableKeyException if the key cannot be recovered (e.g., the given password
   *     is wrong).
   */
  public Key engineGetKey(String alias, char[] password)
      throws NoSuchAlgorithmException, UnrecoverableKeyException {
    permissionCheck();

    // An empty password is rejected by MacOS API, no private key data
    // is exported. If no password is passed (as is the case when
    // this implementation is used as browser keystore in various
    // deployment scenarios like Webstart, JFX and applets), create
    // a dummy password so MacOS API is happy.
    if (password == null || password.length == 0) {
      // Must not be a char array with only a 0, as this is an empty
      // string.
      if (random == null) {
        random = new SecureRandom();
      }
      password = Long.toString(random.nextLong()).toCharArray();
    }

    Object entry = entries.get(alias.toLowerCase());

    if (entry == null || !(entry instanceof KeyEntry)) {
      return null;
    }

    // This call gives us a PKCS12 bag, with the key inside it.
    byte[] exportedKeyInfo = _getEncodedKeyData(((KeyEntry) entry).keyRef, password);
    if (exportedKeyInfo == null) {
      return null;
    }

    PrivateKey returnValue = null;

    try {
      byte[] pkcs8KeyData = fetchPrivateKeyFromBag(exportedKeyInfo);
      byte[] encryptedKey;
      AlgorithmParameters algParams;
      ObjectIdentifier algOid;
      try {
        // get the encrypted private key
        EncryptedPrivateKeyInfo encrInfo = new EncryptedPrivateKeyInfo(pkcs8KeyData);
        encryptedKey = encrInfo.getEncryptedData();

        // parse Algorithm parameters
        DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
        DerInputStream in = val.toDerInputStream();
        algOid = in.getOID();
        algParams = parseAlgParameters(in);

      } catch (IOException ioe) {
        UnrecoverableKeyException uke =
            new UnrecoverableKeyException(
                "Private key not stored as " + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
        uke.initCause(ioe);
        throw uke;
      }

      // Use JCE to decrypt the data using the supplied password.
      SecretKey skey = getPBEKey(password);
      Cipher cipher = Cipher.getInstance(algOid.toString());
      cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
      byte[] decryptedPrivateKey = cipher.doFinal(encryptedKey);
      PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(decryptedPrivateKey);

      // Parse the key algorithm and then use a JCA key factory to create the private key.
      DerValue val = new DerValue(decryptedPrivateKey);
      DerInputStream in = val.toDerInputStream();

      // Ignore this -- version should be 0.
      int i = in.getInteger();

      // Get the Algorithm ID next
      DerValue[] value = in.getSequence(2);
      AlgorithmId algId = new AlgorithmId(value[0].getOID());
      String algName = algId.getName();

      // Get a key factory for this algorithm.  It's likely to be 'RSA'.
      KeyFactory kfac = KeyFactory.getInstance(algName);
      returnValue = kfac.generatePrivate(kspec);
    } catch (Exception e) {
      UnrecoverableKeyException uke =
          new UnrecoverableKeyException("Get Key failed: " + e.getMessage());
      uke.initCause(e);
      throw uke;
    }

    return returnValue;
  }