| /* | |
| * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.security.mscapi; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.OutputStream; | |
| import java.security.InvalidKeyException; | |
| import java.security.Key; | |
| import java.security.KeyStoreSpi; | |
| import java.security.KeyStoreException; | |
| import java.security.UnrecoverableKeyException; | |
| import java.security.NoSuchAlgorithmException; | |
| import java.security.cert.X509Certificate; | |
| import java.security.cert.Certificate; | |
| import java.security.cert.CertificateException; | |
| import java.security.cert.CertificateFactory; | |
| import java.security.interfaces.RSAPrivateCrtKey; | |
| import java.util.*; | |
| import sun.security.util.Debug; | |
| /** | |
| * Implementation of key store for Windows using the Microsoft Crypto API. | |
| * | |
| * @since 1.6 | |
| */ | |
| abstract class CKeyStore extends KeyStoreSpi { | |
| private static final int LOCATION_CURRENTUSER = 0; | |
| private static final int LOCATION_LOCALMACHINE = 1; | |
| public static final class MY extends CKeyStore { | |
| public MY() { | |
| super("MY", LOCATION_CURRENTUSER); | |
| } | |
| } | |
| public static final class ROOT extends CKeyStore { | |
| public ROOT() { | |
| super("ROOT", LOCATION_CURRENTUSER); | |
| } | |
| } | |
| public static final class MYLocalMachine extends CKeyStore { | |
| public MYLocalMachine() { | |
| super("MY", LOCATION_LOCALMACHINE); | |
| } | |
| } | |
| public static final class ROOTLocalMachine extends CKeyStore { | |
| public ROOTLocalMachine() { | |
| super("ROOT", LOCATION_LOCALMACHINE); | |
| } | |
| } | |
| class KeyEntry { | |
| private CKey privateKey; | |
| private X509Certificate[] certChain; | |
| private String alias; | |
| KeyEntry(CKey key, X509Certificate[] chain) { | |
| this(null, key, chain); | |
| } | |
| KeyEntry(String alias, CKey key, X509Certificate[] chain) { | |
| this.privateKey = key; | |
| this.certChain = chain; | |
| /* | |
| * The default alias for both entry types is derived from a | |
| * hash value intrinsic to the first certificate in the chain. | |
| */ | |
| if (alias == null) { | |
| this.alias = Integer.toString(chain[0].hashCode()); | |
| } else { | |
| this.alias = alias; | |
| } | |
| } | |
| /** | |
| * Gets the alias for the keystore entry. | |
| */ | |
| String getAlias() { | |
| return alias; | |
| } | |
| /** | |
| * Sets the alias for the keystore entry. | |
| */ | |
| void setAlias(String alias) { | |
| // TODO - set friendly name prop in cert store | |
| this.alias = alias; | |
| } | |
| /** | |
| * Gets the private key for the keystore entry. | |
| */ | |
| CKey getPrivateKey() { | |
| return privateKey; | |
| } | |
| /** | |
| * Sets the private key for the keystore entry. | |
| */ | |
| void setRSAPrivateKey(Key k) | |
| throws InvalidKeyException, KeyStoreException { | |
| RSAPrivateCrtKey key = (RSAPrivateCrtKey) k; | |
| byte[] modulusBytes = key.getModulus().toByteArray(); | |
| // Adjust key length due to sign bit | |
| int keyBitLength = (modulusBytes[0] == 0) | |
| ? (modulusBytes.length - 1) * 8 | |
| : modulusBytes.length * 8; | |
| byte[] keyBlob = generateRSAPrivateKeyBlob( | |
| keyBitLength, | |
| modulusBytes, | |
| key.getPublicExponent().toByteArray(), | |
| key.getPrivateExponent().toByteArray(), | |
| key.getPrimeP().toByteArray(), | |
| key.getPrimeQ().toByteArray(), | |
| key.getPrimeExponentP().toByteArray(), | |
| key.getPrimeExponentQ().toByteArray(), | |
| key.getCrtCoefficient().toByteArray()); | |
| privateKey = storePrivateKey("RSA", Objects.requireNonNull(keyBlob), | |
| "{" + UUID.randomUUID().toString() + "}", keyBitLength); | |
| } | |
| /** | |
| * Gets the certificate chain for the keystore entry. | |
| */ | |
| X509Certificate[] getCertificateChain() { | |
| return certChain; | |
| } | |
| /** | |
| * Sets the certificate chain for the keystore entry. | |
| */ | |
| void setCertificateChain(X509Certificate[] chain) | |
| throws CertificateException, KeyStoreException { | |
| for (int i = 0; i < chain.length; i++) { | |
| byte[] encoding = chain[i].getEncoded(); | |
| if (i == 0 && privateKey != null) { | |
| storeCertificate(getName(), alias, encoding, | |
| encoding.length, privateKey.getHCryptProvider(), | |
| privateKey.getHCryptKey()); | |
| } else { | |
| storeCertificate(getName(), alias, encoding, | |
| encoding.length, 0L, 0L); // no private key to attach | |
| } | |
| } | |
| certChain = chain; | |
| } | |
| public void delete() throws KeyStoreException { | |
| // Get end-entity certificate and remove from system cert store | |
| X509Certificate[] certChain = getCertificateChain(); | |
| if (certChain != null && certChain.length > 0) { | |
| try { | |
| byte[] encoding = certChain[0].getEncoded(); | |
| removeCertificate(getName(), getAlias(), encoding, | |
| encoding.length); | |
| } catch (CertificateException e) { | |
| throw new KeyStoreException("Cannot remove entry: ", e); | |
| } | |
| } | |
| CKey privateKey = getPrivateKey(); | |
| if (privateKey != null) { | |
| if (privateKey.getHCryptKey() != 0) { | |
| destroyKeyContainer( | |
| CKey.getContainerName(privateKey.getHCryptProvider())); | |
| } else { | |
| removeCngKey(privateKey.getHCryptProvider()); | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| * An X.509 certificate factory. | |
| * Used to create an X.509 certificate from its DER-encoding. | |
| */ | |
| private CertificateFactory certificateFactory = null; | |
| /* | |
| * Compatibility mode: for applications that assume keystores are | |
| * stream-based this mode tolerates (but ignores) a non-null stream | |
| * or password parameter when passed to the load or store methods. | |
| * The mode is enabled by default. | |
| */ | |
| private static final String KEYSTORE_COMPATIBILITY_MODE_PROP = | |
| "sun.security.mscapi.keyStoreCompatibilityMode"; | |
| private final boolean keyStoreCompatibilityMode; | |
| private static final Debug debug = Debug.getInstance("keystore"); | |
| /* | |
| * The keystore entries. | |
| * Keys in the map are unique aliases (thus can differ from | |
| * KeyEntry.getAlias()) | |
| */ | |
| private Map<String,KeyEntry> entries = new HashMap<>(); | |
| /* | |
| * The keystore name. | |
| * Case is not significant. | |
| */ | |
| private final String storeName; | |
| /* | |
| * The keystore location. | |
| */ | |
| private final int storeLocation; | |
| CKeyStore(String storeName, int storeLocation) { | |
| // Get the compatibility mode | |
| String prop = System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP); | |
| if ("false".equalsIgnoreCase(prop)) { | |
| keyStoreCompatibilityMode = false; | |
| } else { | |
| keyStoreCompatibilityMode = true; | |
| } | |
| this.storeName = storeName; | |
| this.storeLocation = storeLocation; | |
| } | |
| /** | |
| * Returns the key associated with the given alias. | |
| * <p> | |
| * A compatibility mode is supported for applications that assume | |
| * a password must be supplied. It permits (but ignores) a non-null | |
| * <code>password</code>. The mode is enabled by default. | |
| * Set the | |
| * <code>sun.security.mscapi.keyStoreCompatibilityMode</code> | |
| * system property to <code>false</code> to disable compatibility mode | |
| * and reject a non-null <code>password</code>. | |
| * | |
| * @param alias the alias name | |
| * @param password the password, which should be <code>null</code> | |
| * | |
| * @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, | |
| * or if compatibility mode is disabled and <code>password</code> is | |
| * non-null. | |
| * @exception UnrecoverableKeyException if the key cannot be recovered. | |
| */ | |
| public java.security.Key engineGetKey(String alias, char[] password) | |
| throws NoSuchAlgorithmException, UnrecoverableKeyException { | |
| if (alias == null) { | |
| return null; | |
| } | |
| if (password != null && !keyStoreCompatibilityMode) { | |
| throw new UnrecoverableKeyException("Password must be null"); | |
| } | |
| if (engineIsKeyEntry(alias) == false) | |
| return null; | |
| KeyEntry entry = entries.get(alias); | |
| return (entry == null) | |
| ? null | |
| : entry.getPrivateKey(); | |
| } | |
| /** | |
| * 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) { | |
| if (alias == null) { | |
| return null; | |
| } | |
| KeyEntry entry = entries.get(alias); | |
| X509Certificate[] certChain = (entry == null) | |
| ? null | |
| : entry.getCertificateChain(); | |
| return (certChain == null) | |
| ? null | |
| : certChain.clone(); | |
| } | |
| /** | |
| * 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) { | |
| if (alias == null) { | |
| return null; | |
| } | |
| KeyEntry entry = entries.get(alias); | |
| X509Certificate[] certChain = (entry == null) | |
| ? null | |
| : entry.getCertificateChain(); | |
| return (certChain == null || certChain.length == 0) | |
| ? null | |
| : certChain[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) { | |
| if (alias == null) { | |
| return null; | |
| } | |
| return new Date(); | |
| } | |
| /** | |
| * Stores the given private key and associated certificate chain in the | |
| * keystore. | |
| * | |
| * <p>The given java.security.PrivateKey <code>key</code> 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 certificate | |
| * chain. Otherwise, a new entry is created. | |
| * | |
| * <p> | |
| * A compatibility mode is supported for applications that assume | |
| * a password must be supplied. It permits (but ignores) a non-null | |
| * <code>password</code>. The mode is enabled by default. | |
| * Set the | |
| * <code>sun.security.mscapi.keyStoreCompatibilityMode</code> | |
| * system property to <code>false</code> to disable compatibility mode | |
| * and reject a non-null <code>password</code>. | |
| * | |
| * @param alias the alias name | |
| * @param key the private key to be associated with the alias | |
| * @param password the password, which should be <code>null</code> | |
| * @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 is not a private key, | |
| * cannot be protected, or if compatibility mode is disabled and | |
| * <code>password</code> is non-null, or if this operation fails for | |
| * some other reason. | |
| */ | |
| public void engineSetKeyEntry(String alias, java.security.Key key, | |
| char[] password, Certificate[] chain) throws KeyStoreException { | |
| if (alias == null) { | |
| throw new KeyStoreException("alias must not be null"); | |
| } | |
| if (password != null && !keyStoreCompatibilityMode) { | |
| throw new KeyStoreException("Password must be null"); | |
| } | |
| if (key instanceof RSAPrivateCrtKey) { | |
| X509Certificate[] xchain; | |
| if (chain != null) { | |
| if (chain instanceof X509Certificate[]) { | |
| xchain = (X509Certificate[]) chain; | |
| } else { | |
| xchain = new X509Certificate[chain.length]; | |
| System.arraycopy(chain, 0, xchain, 0, chain.length); | |
| } | |
| } else { | |
| xchain = null; | |
| } | |
| KeyEntry oldEntry = entries.get(alias); | |
| try { | |
| KeyEntry newEntry = new KeyEntry(alias, null, xchain); | |
| newEntry.setRSAPrivateKey(key); | |
| newEntry.setCertificateChain(xchain); | |
| entries.put(alias, newEntry); | |
| } catch (CertificateException | InvalidKeyException e) { | |
| throw new KeyStoreException(e); | |
| } | |
| if (oldEntry != null) { | |
| oldEntry.delete(); | |
| } | |
| } else { | |
| throw new UnsupportedOperationException( | |
| "Cannot assign the key to the given alias."); | |
| } | |
| } | |
| /** | |
| * 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 { | |
| throw new UnsupportedOperationException( | |
| "Cannot assign the encoded key to the given alias."); | |
| } | |
| /** | |
| * 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 { | |
| if (alias == null) { | |
| throw new KeyStoreException("alias must not be null"); | |
| } | |
| if (cert instanceof X509Certificate) { | |
| // TODO - build CryptoAPI chain? | |
| X509Certificate[] chain = | |
| new X509Certificate[]{ (X509Certificate) cert }; | |
| KeyEntry oldEntry = entries.get(alias); | |
| if (oldEntry != null && oldEntry.privateKey != null) { | |
| throw new KeyStoreException("Cannot overwrite key entry"); | |
| } | |
| try { | |
| KeyEntry newEntry = new KeyEntry(alias, null, chain); | |
| newEntry.setCertificateChain(chain); | |
| entries.put(alias, newEntry); | |
| } catch (CertificateException ce) { | |
| throw new KeyStoreException(ce); | |
| } | |
| if (oldEntry != null) { | |
| oldEntry.delete(); | |
| } | |
| } else { | |
| throw new UnsupportedOperationException( | |
| "Cannot assign the certificate to the given alias."); | |
| } | |
| } | |
| /** | |
| * 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 { | |
| if (alias == null) { | |
| throw new KeyStoreException("alias must not be null"); | |
| } | |
| KeyEntry entry = entries.remove(alias); | |
| if (entry != null) { | |
| entry.delete(); | |
| } | |
| } | |
| /** | |
| * Lists all the alias names of this keystore. | |
| * | |
| * @return enumeration of the alias names | |
| */ | |
| public Enumeration<String> engineAliases() { | |
| final Iterator<String> iter = entries.keySet().iterator(); | |
| return new Enumeration<String>() { | |
| public boolean hasMoreElements() { | |
| return iter.hasNext(); | |
| } | |
| public String nextElement() { | |
| return iter.next(); | |
| } | |
| }; | |
| } | |
| /** | |
| * 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) { | |
| return entries.containsKey(alias); | |
| } | |
| /** | |
| * Retrieves the number of entries in this keystore. | |
| * | |
| * @return the number of entries in this keystore | |
| */ | |
| public int engineSize() { | |
| return entries.size(); | |
| } | |
| /** | |
| * Returns true if the entry identified by the given alias is a | |
| * <i>key entry</i>, and false otherwise. | |
| * | |
| * @return true if the entry identified by the given alias is a | |
| * <i>key entry</i>, false otherwise. | |
| */ | |
| public boolean engineIsKeyEntry(String alias) { | |
| if (alias == null) { | |
| return false; | |
| } | |
| KeyEntry entry = entries.get(alias); | |
| return entry != null && entry.getPrivateKey() != null; | |
| } | |
| /** | |
| * 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) { | |
| if (alias == null) { | |
| return false; | |
| } | |
| KeyEntry entry = entries.get(alias); | |
| return entry != null && entry.getPrivateKey() == null; | |
| } | |
| /** | |
| * 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) { | |
| for (Map.Entry<String,KeyEntry> mapEntry : entries.entrySet()) { | |
| KeyEntry entry = mapEntry.getValue(); | |
| if (entry.certChain != null && | |
| entry.certChain.length > 0 && | |
| entry.certChain[0].equals(cert)) { | |
| return mapEntry.getKey(); | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * engineStore is currently a no-op. | |
| * Entries are stored during engineSetEntry. | |
| * | |
| * A compatibility mode is supported for applications that assume | |
| * keystores are stream-based. It permits (but ignores) a non-null | |
| * <code>stream</code> or <code>password</code>. | |
| * The mode is enabled by default. | |
| * Set the | |
| * <code>sun.security.mscapi.keyStoreCompatibilityMode</code> | |
| * system property to <code>false</code> to disable compatibility mode | |
| * and reject a non-null <code>stream</code> or <code>password</code>. | |
| * | |
| * @param stream the output stream, which should be <code>null</code> | |
| * @param password the password, which should be <code>null</code> | |
| * | |
| * @exception IOException if compatibility mode is disabled and either | |
| * parameter is non-null. | |
| */ | |
| public void engineStore(OutputStream stream, char[] password) | |
| throws IOException, NoSuchAlgorithmException, CertificateException { | |
| if (stream != null && !keyStoreCompatibilityMode) { | |
| throw new IOException("Keystore output stream must be null"); | |
| } | |
| if (password != null && !keyStoreCompatibilityMode) { | |
| throw new IOException("Keystore password must be null"); | |
| } | |
| } | |
| /** | |
| * Loads the keystore. | |
| * | |
| * A compatibility mode is supported for applications that assume | |
| * keystores are stream-based. It permits (but ignores) a non-null | |
| * <code>stream</code> or <code>password</code>. | |
| * The mode is enabled by default. | |
| * Set the | |
| * <code>sun.security.mscapi.keyStoreCompatibilityMode</code> | |
| * system property to <code>false</code> to disable compatibility mode | |
| * and reject a non-null <code>stream</code> or <code>password</code>. | |
| * | |
| * @param stream the input stream, which should be <code>null</code>. | |
| * @param password the password, which should be <code>null</code>. | |
| * | |
| * @exception IOException if there is an I/O or format problem with the | |
| * keystore data. Or if compatibility mode is disabled and either | |
| * parameter is non-null. | |
| * @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 { | |
| if (stream != null && !keyStoreCompatibilityMode) { | |
| throw new IOException("Keystore input stream must be null"); | |
| } | |
| if (password != null && !keyStoreCompatibilityMode) { | |
| throw new IOException("Keystore password must be null"); | |
| } | |
| // Clear all key entries | |
| entries.clear(); | |
| try { | |
| // Load keys and/or certificate chains | |
| loadKeysOrCertificateChains(getName(), getLocation()); | |
| } catch (KeyStoreException e) { | |
| throw new IOException(e); | |
| } | |
| if (debug != null) { | |
| debug.println("MSCAPI keystore load: entry count: " + | |
| entries.size()); | |
| } | |
| } | |
| /** | |
| * Stores the given entry into the map, making sure | |
| * the alias, used as the key is unique. | |
| * If the same alias already exists, it tries to append | |
| * a suffix (1), (2), etc to it until it finds a unique | |
| * value. | |
| */ | |
| private void storeWithUniqueAlias(String alias, KeyEntry entry) { | |
| String uniqAlias = alias; | |
| int uniqNum = 1; | |
| while (true) { | |
| if (entries.putIfAbsent(uniqAlias, entry) == null) { | |
| break; | |
| } | |
| uniqAlias = alias + " (" + (uniqNum++) + ")"; | |
| } | |
| } | |
| /** | |
| * Generates a certificate chain from the collection of | |
| * certificates and stores the result into a key entry. | |
| * <p> | |
| * This method is called by native codes in security.cpp. | |
| */ | |
| private void generateCertificateChain(String alias, | |
| Collection<? extends Certificate> certCollection) { | |
| try { | |
| X509Certificate[] certChain = | |
| new X509Certificate[certCollection.size()]; | |
| int i = 0; | |
| for (Iterator<? extends Certificate> iter = | |
| certCollection.iterator(); iter.hasNext(); i++) { | |
| certChain[i] = (X509Certificate) iter.next(); | |
| } | |
| storeWithUniqueAlias(alias, | |
| new KeyEntry(alias, null, certChain)); | |
| } catch (Throwable e) { | |
| // Ignore the exception and skip this entry | |
| // If e is thrown, remember to deal with it in | |
| // native code. | |
| } | |
| } | |
| /** | |
| * Generates key and certificate chain from the private key handle, | |
| * collection of certificates and stores the result into key entries. | |
| * <p> | |
| * This method is called by native codes in security.cpp. | |
| */ | |
| private void generateKeyAndCertificateChain(boolean isRSA, String alias, | |
| long hCryptProv, long hCryptKey, int keyLength, | |
| Collection<? extends Certificate> certCollection) { | |
| try { | |
| X509Certificate[] certChain = | |
| new X509Certificate[certCollection.size()]; | |
| int i = 0; | |
| for (Iterator<? extends Certificate> iter = | |
| certCollection.iterator(); iter.hasNext(); i++) { | |
| certChain[i] = (X509Certificate) iter.next(); | |
| } | |
| storeWithUniqueAlias(alias, new KeyEntry(alias, | |
| CPrivateKey.of(isRSA ? "RSA" : "EC", hCryptProv, hCryptKey, keyLength), | |
| certChain)); | |
| } catch (Throwable e) { | |
| // Ignore the exception and skip this entry | |
| // If e is thrown, remember to deal with it in | |
| // native code. | |
| } | |
| } | |
| /** | |
| * Generates certificates from byte data and stores into cert collection. | |
| * <p> | |
| * This method is called by native codes in security.cpp. | |
| * | |
| * @param data Byte data. | |
| * @param certCollection Collection of certificates. | |
| */ | |
| private void generateCertificate(byte[] data, | |
| Collection<Certificate> certCollection) { | |
| try { | |
| ByteArrayInputStream bis = new ByteArrayInputStream(data); | |
| // Obtain certificate factory | |
| if (certificateFactory == null) { | |
| certificateFactory = CertificateFactory.getInstance("X.509", "SUN"); | |
| } | |
| // Generate certificate | |
| Collection<? extends Certificate> c = | |
| certificateFactory.generateCertificates(bis); | |
| certCollection.addAll(c); | |
| } catch (Throwable te) { | |
| // Ignore the exception and skip this certificate | |
| // If e is thrown, remember to deal with it in | |
| // native code. | |
| } | |
| } | |
| /** | |
| * Returns the name of the keystore. | |
| */ | |
| private String getName() { | |
| return storeName; | |
| } | |
| /** | |
| * Returns the location of the keystore. | |
| */ | |
| private int getLocation() { | |
| return storeLocation; | |
| } | |
| /** | |
| * Loads keys and/or certificates from keystore into Collection. | |
| * | |
| * @param name Name of keystore. | |
| * @param location Location of keystore. | |
| */ | |
| private native void loadKeysOrCertificateChains(String name, | |
| int location) throws KeyStoreException; | |
| /** | |
| * Stores a DER-encoded certificate into the certificate store | |
| * | |
| * @param name Name of the keystore. | |
| * @param alias Name of the certificate. | |
| * @param encoding DER-encoded certificate. | |
| */ | |
| private native void storeCertificate(String name, String alias, | |
| byte[] encoding, int encodingLength, long hCryptProvider, | |
| long hCryptKey) throws CertificateException, KeyStoreException; | |
| /** | |
| * Removes the certificate from the certificate store | |
| * | |
| * @param name Name of the keystore. | |
| * @param alias Name of the certificate. | |
| * @param encoding DER-encoded certificate. | |
| */ | |
| private native void removeCertificate(String name, String alias, | |
| byte[] encoding, int encodingLength) | |
| throws CertificateException, KeyStoreException; | |
| /** | |
| * Destroys the key container. | |
| * | |
| * @param keyContainerName The name of the key container. | |
| */ | |
| private native void destroyKeyContainer(String keyContainerName) | |
| throws KeyStoreException; | |
| /** | |
| * Removes a CNG key. | |
| * | |
| * @param k the handle of the key | |
| */ | |
| private native void removeCngKey(long k) throws KeyStoreException; | |
| /** | |
| * Generates a private-key BLOB from a key's components. | |
| */ | |
| private native byte[] generateRSAPrivateKeyBlob( | |
| int keyBitLength, | |
| byte[] modulus, | |
| byte[] publicExponent, | |
| byte[] privateExponent, | |
| byte[] primeP, | |
| byte[] primeQ, | |
| byte[] exponentP, | |
| byte[] exponentQ, | |
| byte[] crtCoefficient) throws InvalidKeyException; | |
| private native CPrivateKey storePrivateKey(String alg, byte[] keyBlob, | |
| String keyContainerName, int keySize) throws KeyStoreException; | |
| } |