Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/security/mscapi/CKeyStore.java
32288 views
/*1* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.mscapi;2627import java.io.ByteArrayInputStream;28import java.io.IOException;29import java.io.InputStream;30import java.io.OutputStream;31import java.security.AccessController;32import java.security.InvalidKeyException;33import java.security.Key;34import java.security.KeyStoreSpi;35import java.security.KeyStoreException;36import java.security.PrivilegedAction;37import java.security.UnrecoverableKeyException;38import java.security.NoSuchAlgorithmException;39import java.security.SecurityPermission;40import java.security.cert.X509Certificate;41import java.security.cert.Certificate;42import java.security.cert.CertificateException;43import java.security.cert.CertificateFactory;44import java.security.interfaces.RSAPrivateCrtKey;45import java.util.*;4647import sun.security.util.Debug;4849/**50* Implementation of key store for Windows using the Microsoft Crypto API.51*52* @since 1.653*/54abstract class CKeyStore extends KeyStoreSpi {5556public static final class MY extends CKeyStore {57public MY() {58super("MY");59}60}6162public static final class ROOT extends CKeyStore {63public ROOT() {64super("ROOT");65}66}6768class KeyEntry {69private CKey privateKey;70private X509Certificate[] certChain;71private String alias;7273KeyEntry(CKey key, X509Certificate[] chain) {74this(null, key, chain);75}7677KeyEntry(String alias, CKey key, X509Certificate[] chain) {78this.privateKey = key;79this.certChain = chain;80/*81* The default alias for both entry types is derived from a82* hash value intrinsic to the first certificate in the chain.83*/84if (alias == null) {85this.alias = Integer.toString(chain[0].hashCode());86} else {87this.alias = alias;88}89}9091/**92* Gets the alias for the keystore entry.93*/94String getAlias() {95return alias;96}9798/**99* Sets the alias for the keystore entry.100*/101void setAlias(String alias) {102// TODO - set friendly name prop in cert store103this.alias = alias;104}105106/**107* Gets the private key for the keystore entry.108*/109CKey getPrivateKey() {110return privateKey;111}112113/**114* Sets the private key for the keystore entry.115*/116void setRSAPrivateKey(Key k)117throws InvalidKeyException, KeyStoreException {118RSAPrivateCrtKey key = (RSAPrivateCrtKey) k;119byte[] modulusBytes = key.getModulus().toByteArray();120121// Adjust key length due to sign bit122int keyBitLength = (modulusBytes[0] == 0)123? (modulusBytes.length - 1) * 8124: modulusBytes.length * 8;125126byte[] keyBlob = generateRSAPrivateKeyBlob(127keyBitLength,128modulusBytes,129key.getPublicExponent().toByteArray(),130key.getPrivateExponent().toByteArray(),131key.getPrimeP().toByteArray(),132key.getPrimeQ().toByteArray(),133key.getPrimeExponentP().toByteArray(),134key.getPrimeExponentQ().toByteArray(),135key.getCrtCoefficient().toByteArray());136137privateKey = storePrivateKey("RSA", Objects.requireNonNull(keyBlob),138"{" + UUID.randomUUID().toString() + "}", keyBitLength);139}140141/**142* Gets the certificate chain for the keystore entry.143*/144X509Certificate[] getCertificateChain() {145return certChain;146}147148/**149* Sets the certificate chain for the keystore entry.150*/151void setCertificateChain(X509Certificate[] chain)152throws CertificateException, KeyStoreException {153for (int i = 0; i < chain.length; i++) {154byte[] encoding = chain[i].getEncoded();155if (i == 0 && privateKey != null) {156storeCertificate(getName(), alias, encoding,157encoding.length, privateKey.getHCryptProvider(),158privateKey.getHCryptKey());159160} else {161storeCertificate(getName(), alias, encoding,162encoding.length, 0L, 0L); // no private key to attach163}164}165certChain = chain;166}167}168169/*170* An X.509 certificate factory.171* Used to create an X.509 certificate from its DER-encoding.172*/173private CertificateFactory certificateFactory = null;174175/*176* Compatibility mode: for applications that assume keystores are177* stream-based this mode tolerates (but ignores) a non-null stream178* or password parameter when passed to the load or store methods.179* The mode is enabled by default.180*/181private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =182"sun.security.mscapi.keyStoreCompatibilityMode";183private final boolean keyStoreCompatibilityMode;184private static final Debug debug = Debug.getInstance("keystore");185186/*187* The keystore entries.188* Keys in the map are unique aliases (thus can differ from189* KeyEntry.getAlias())190*/191private Map<String,KeyEntry> entries = new HashMap<>();192193/*194* The keystore name.195* Case is not significant.196*/197private final String storeName;198199CKeyStore(String storeName) {200// Get the compatibility mode201String prop = AccessController.doPrivileged(202(PrivilegedAction<String>) () -> System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP));203204if ("false".equalsIgnoreCase(prop)) {205keyStoreCompatibilityMode = false;206} else {207keyStoreCompatibilityMode = true;208}209210this.storeName = storeName;211}212213/**214* Returns the key associated with the given alias.215* <p>216* A compatibility mode is supported for applications that assume217* a password must be supplied. It permits (but ignores) a non-null218* <code>password</code>. The mode is enabled by default.219* Set the220* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>221* system property to <code>false</code> to disable compatibility mode222* and reject a non-null <code>password</code>.223*224* @param alias the alias name225* @param password the password, which should be <code>null</code>226*227* @return the requested key, or null if the given alias does not exist228* or does not identify a <i>key entry</i>.229*230* @exception NoSuchAlgorithmException if the algorithm for recovering the231* key cannot be found,232* or if compatibility mode is disabled and <code>password</code> is233* non-null.234* @exception UnrecoverableKeyException if the key cannot be recovered.235*/236public java.security.Key engineGetKey(String alias, char[] password)237throws NoSuchAlgorithmException, UnrecoverableKeyException {238if (alias == null) {239return null;240}241242if (password != null && !keyStoreCompatibilityMode) {243throw new UnrecoverableKeyException("Password must be null");244}245246if (engineIsKeyEntry(alias) == false)247return null;248249KeyEntry entry = entries.get(alias);250return (entry == null)251? null252: entry.getPrivateKey();253}254255/**256* Returns the certificate chain associated with the given alias.257*258* @param alias the alias name259*260* @return the certificate chain (ordered with the user's certificate first261* and the root certificate authority last), or null if the given alias262* does not exist or does not contain a certificate chain (i.e., the given263* alias identifies either a <i>trusted certificate entry</i> or a264* <i>key entry</i> without a certificate chain).265*/266public Certificate[] engineGetCertificateChain(String alias) {267if (alias == null) {268return null;269}270271KeyEntry entry = entries.get(alias);272X509Certificate[] certChain = (entry == null)273? null274: entry.getCertificateChain();275return (certChain == null)276? null277: certChain.clone();278}279280/**281* Returns the certificate associated with the given alias.282*283* <p>If the given alias name identifies a284* <i>trusted certificate entry</i>, the certificate associated with that285* entry is returned. If the given alias name identifies a286* <i>key entry</i>, the first element of the certificate chain of that287* entry is returned, or null if that entry does not have a certificate288* chain.289*290* @param alias the alias name291*292* @return the certificate, or null if the given alias does not exist or293* does not contain a certificate.294*/295public Certificate engineGetCertificate(String alias) {296if (alias == null) {297return null;298}299300KeyEntry entry = entries.get(alias);301X509Certificate[] certChain = (entry == null)302? null303: entry.getCertificateChain();304return (certChain == null || certChain.length == 0)305? null306: certChain[0];307}308309/**310* Returns the creation date of the entry identified by the given alias.311*312* @param alias the alias name313*314* @return the creation date of this entry, or null if the given alias does315* not exist316*/317public Date engineGetCreationDate(String alias) {318if (alias == null) {319return null;320}321return new Date();322}323324/**325* Stores the given private key and associated certificate chain in the326* keystore.327*328* <p>The given java.security.PrivateKey <code>key</code> must329* be accompanied by a certificate chain certifying the330* corresponding public key.331*332* <p>If the given alias already exists, the keystore information333* associated with it is overridden by the given key and certificate334* chain. Otherwise, a new entry is created.335*336* <p>337* A compatibility mode is supported for applications that assume338* a password must be supplied. It permits (but ignores) a non-null339* <code>password</code>. The mode is enabled by default.340* Set the341* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>342* system property to <code>false</code> to disable compatibility mode343* and reject a non-null <code>password</code>.344*345* @param alias the alias name346* @param key the private key to be associated with the alias347* @param password the password, which should be <code>null</code>348* @param chain the certificate chain for the corresponding public349* key (only required if the given key is of type350* <code>java.security.PrivateKey</code>).351*352* @exception KeyStoreException if the given key is not a private key,353* cannot be protected, or if compatibility mode is disabled and354* <code>password</code> is non-null, or if this operation fails for355* some other reason.356*/357public void engineSetKeyEntry(String alias, java.security.Key key,358char[] password, Certificate[] chain) throws KeyStoreException {359if (alias == null) {360throw new KeyStoreException("alias must not be null");361}362363if (password != null && !keyStoreCompatibilityMode) {364throw new KeyStoreException("Password must be null");365}366367if (key instanceof RSAPrivateCrtKey) {368369KeyEntry entry = entries.get(alias);370371X509Certificate[] xchain;372if (chain != null) {373if (chain instanceof X509Certificate[]) {374xchain = (X509Certificate[]) chain;375} else {376xchain = new X509Certificate[chain.length];377System.arraycopy(chain, 0, xchain, 0, chain.length);378}379} else {380xchain = null;381}382383if (entry == null) {384entry =385//TODO new KeyEntry(alias, key, (X509Certificate[]) chain);386new KeyEntry(alias, null, xchain);387storeWithUniqueAlias(alias, entry);388}389390entry.setAlias(alias);391392try {393entry.setRSAPrivateKey(key);394entry.setCertificateChain(xchain);395396} catch (CertificateException ce) {397throw new KeyStoreException(ce);398399} catch (InvalidKeyException ike) {400throw new KeyStoreException(ike);401}402403} else {404throw new UnsupportedOperationException(405"Cannot assign the key to the given alias.");406}407}408409/**410* Assigns the given key (that has already been protected) to the given411* alias.412*413* <p>If the protected key is of type414* <code>java.security.PrivateKey</code>, it must be accompanied by a415* certificate chain certifying the corresponding public key. If the416* underlying keystore implementation is of type <code>jks</code>,417* <code>key</code> must be encoded as an418* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.419*420* <p>If the given alias already exists, the keystore information421* associated with it is overridden by the given key (and possibly422* certificate chain).423*424* @param alias the alias name425* @param key the key (in protected format) to be associated with the alias426* @param chain the certificate chain for the corresponding public427* key (only useful if the protected key is of type428* <code>java.security.PrivateKey</code>).429*430* @exception KeyStoreException if this operation fails.431*/432public void engineSetKeyEntry(String alias, byte[] key,433Certificate[] chain)434throws KeyStoreException {435throw new UnsupportedOperationException(436"Cannot assign the encoded key to the given alias.");437}438439/**440* Assigns the given certificate to the given alias.441*442* <p>If the given alias already exists in this keystore and identifies a443* <i>trusted certificate entry</i>, the certificate associated with it is444* overridden by the given certificate.445*446* @param alias the alias name447* @param cert the certificate448*449* @exception KeyStoreException if the given alias already exists and does450* not identify a <i>trusted certificate entry</i>, or this operation451* fails for some other reason.452*/453public void engineSetCertificateEntry(String alias, Certificate cert)454throws KeyStoreException {455if (alias == null) {456throw new KeyStoreException("alias must not be null");457}458459if (cert instanceof X509Certificate) {460461// TODO - build CryptoAPI chain?462X509Certificate[] chain =463new X509Certificate[]{ (X509Certificate) cert };464KeyEntry entry = entries.get(alias);465466if (entry == null) {467entry =468new KeyEntry(alias, null, chain);469storeWithUniqueAlias(alias, entry);470}471472if (entry.getPrivateKey() == null) { // trusted-cert entry473entry.setAlias(alias);474475try {476entry.setCertificateChain(chain);477478} catch (CertificateException ce) {479throw new KeyStoreException(ce);480}481}482483} else {484throw new UnsupportedOperationException(485"Cannot assign the certificate to the given alias.");486}487}488489/**490* Deletes the entry identified by the given alias from this keystore.491*492* @param alias the alias name493*494* @exception KeyStoreException if the entry cannot be removed.495*/496public void engineDeleteEntry(String alias) throws KeyStoreException {497if (alias == null) {498throw new KeyStoreException("alias must not be null");499}500501KeyEntry entry = entries.remove(alias);502if (entry != null) {503// Get end-entity certificate and remove from system cert store504X509Certificate[] certChain = entry.getCertificateChain();505if (certChain != null && certChain.length > 0) {506507try {508509byte[] encoding = certChain[0].getEncoded();510removeCertificate(getName(), entry.getAlias(), encoding,511encoding.length);512513} catch (CertificateException e) {514throw new KeyStoreException("Cannot remove entry: ", e);515}516}517CKey privateKey = entry.getPrivateKey();518if (privateKey != null) {519destroyKeyContainer(520CKey.getContainerName(privateKey.getHCryptProvider()));521}522}523}524525/**526* Lists all the alias names of this keystore.527*528* @return enumeration of the alias names529*/530public Enumeration<String> engineAliases() {531final Iterator<String> iter = entries.keySet().iterator();532533return new Enumeration<String>() {534public boolean hasMoreElements() {535return iter.hasNext();536}537538public String nextElement() {539return iter.next();540}541};542}543544/**545* Checks if the given alias exists in this keystore.546*547* @param alias the alias name548*549* @return true if the alias exists, false otherwise550*/551public boolean engineContainsAlias(String alias) {552return entries.containsKey(alias);553}554555/**556* Retrieves the number of entries in this keystore.557*558* @return the number of entries in this keystore559*/560public int engineSize() {561return entries.size();562}563564/**565* Returns true if the entry identified by the given alias is a566* <i>key entry</i>, and false otherwise.567*568* @return true if the entry identified by the given alias is a569* <i>key entry</i>, false otherwise.570*/571public boolean engineIsKeyEntry(String alias) {572573if (alias == null) {574return false;575}576577KeyEntry entry = entries.get(alias);578return entry != null && entry.getPrivateKey() != null;579}580581/**582* Returns true if the entry identified by the given alias is a583* <i>trusted certificate entry</i>, and false otherwise.584*585* @return true if the entry identified by the given alias is a586* <i>trusted certificate entry</i>, false otherwise.587*/588public boolean engineIsCertificateEntry(String alias) {589590if (alias == null) {591return false;592}593594KeyEntry entry = entries.get(alias);595return entry != null && entry.getPrivateKey() == null;596}597598/**599* Returns the (alias) name of the first keystore entry whose certificate600* matches the given certificate.601*602* <p>This method attempts to match the given certificate with each603* keystore entry. If the entry being considered604* is a <i>trusted certificate entry</i>, the given certificate is605* compared to that entry's certificate. If the entry being considered is606* a <i>key entry</i>, the given certificate is compared to the first607* element of that entry's certificate chain (if a chain exists).608*609* @param cert the certificate to match with.610*611* @return the (alias) name of the first entry with matching certificate,612* or null if no such entry exists in this keystore.613*/614public String engineGetCertificateAlias(Certificate cert) {615616for (Map.Entry<String,KeyEntry> mapEntry : entries.entrySet()) {617KeyEntry entry = mapEntry.getValue();618if (entry.certChain != null &&619entry.certChain.length > 0 &&620entry.certChain[0].equals(cert)) {621return entry.getAlias();622}623}624625return null;626}627628/**629* engineStore is currently a no-op.630* Entries are stored during engineSetEntry.631*632* A compatibility mode is supported for applications that assume633* keystores are stream-based. It permits (but ignores) a non-null634* <code>stream</code> or <code>password</code>.635* The mode is enabled by default.636* Set the637* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>638* system property to <code>false</code> to disable compatibility mode639* and reject a non-null <code>stream</code> or <code>password</code>.640*641* @param stream the output stream, which should be <code>null</code>642* @param password the password, which should be <code>null</code>643*644* @exception IOException if compatibility mode is disabled and either645* parameter is non-null.646*/647public void engineStore(OutputStream stream, char[] password)648throws IOException, NoSuchAlgorithmException, CertificateException {649if (stream != null && !keyStoreCompatibilityMode) {650throw new IOException("Keystore output stream must be null");651}652653if (password != null && !keyStoreCompatibilityMode) {654throw new IOException("Keystore password must be null");655}656}657658/**659* Loads the keystore.660*661* A compatibility mode is supported for applications that assume662* keystores are stream-based. It permits (but ignores) a non-null663* <code>stream</code> or <code>password</code>.664* The mode is enabled by default.665* Set the666* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>667* system property to <code>false</code> to disable compatibility mode668* and reject a non-null <code>stream</code> or <code>password</code>.669*670* @param stream the input stream, which should be <code>null</code>.671* @param password the password, which should be <code>null</code>.672*673* @exception IOException if there is an I/O or format problem with the674* keystore data. Or if compatibility mode is disabled and either675* parameter is non-null.676* @exception NoSuchAlgorithmException if the algorithm used to check677* the integrity of the keystore cannot be found678* @exception CertificateException if any of the certificates in the679* keystore could not be loaded680* @exception SecurityException if the security check for681* <code>SecurityPermission("authProvider.<i>name</i>")</code> does not682* pass, where <i>name</i> is the value returned by683* this provider's <code>getName</code> method.684*/685public void engineLoad(InputStream stream, char[] password)686throws IOException, NoSuchAlgorithmException, CertificateException {687if (stream != null && !keyStoreCompatibilityMode) {688throw new IOException("Keystore input stream must be null");689}690691if (password != null && !keyStoreCompatibilityMode) {692throw new IOException("Keystore password must be null");693}694695/*696* Use the same security check as AuthProvider.login697*/698SecurityManager sm = System.getSecurityManager();699if (sm != null) {700sm.checkPermission(new SecurityPermission(701"authProvider.SunMSCAPI"));702}703704// Clear all key entries705entries.clear();706707try {708709// Load keys and/or certificate chains710loadKeysOrCertificateChains(getName());711712} catch (KeyStoreException e) {713throw new IOException(e);714}715716if (debug != null) {717debug.println("MSCAPI keystore load: entry count: " +718entries.size());719}720}721722/**723* Stores the given entry into the map, making sure724* the alias, used as the key is unique.725* If the same alias already exists, it tries to append726* a suffix (1), (2), etc to it until it finds a unique727* value.728*/729private void storeWithUniqueAlias(String alias, KeyEntry entry) {730String uniqAlias = alias;731int uniqNum = 1;732733while (true) {734if (entries.putIfAbsent(uniqAlias, entry) == null) {735break;736}737uniqAlias = alias + " (" + (uniqNum++) + ")";738}739}740741742/**743* Generates a certificate chain from the collection of744* certificates and stores the result into a key entry.745* <p>746* This method is called by native codes in security.cpp.747*/748private void generateCertificateChain(String alias,749Collection<? extends Certificate> certCollection) {750try {751X509Certificate[] certChain =752new X509Certificate[certCollection.size()];753754int i = 0;755for (Iterator<? extends Certificate> iter =756certCollection.iterator(); iter.hasNext(); i++) {757certChain[i] = (X509Certificate) iter.next();758}759760storeWithUniqueAlias(alias,761new KeyEntry(alias, null, certChain));762} catch (Throwable e) {763// Ignore the exception and skip this entry764// TODO - throw CertificateException?765}766}767768/**769* Generates key and certificate chain from the private key handle,770* collection of certificates and stores the result into key entries.771* <p>772* This method is called by native codes in security.cpp.773*/774private void generateKeyAndCertificateChain(boolean isRSA, String alias,775long hCryptProv, long hCryptKey, int keyLength,776Collection<? extends Certificate> certCollection) {777try {778X509Certificate[] certChain =779new X509Certificate[certCollection.size()];780781int i = 0;782for (Iterator<? extends Certificate> iter =783certCollection.iterator(); iter.hasNext(); i++) {784certChain[i] = (X509Certificate) iter.next();785}786storeWithUniqueAlias(alias, new KeyEntry(alias,787CPrivateKey.of(isRSA ? "RSA" : "EC", hCryptProv, hCryptKey, keyLength),788certChain));789} catch (Throwable e) {790// Ignore the exception and skip this entry791// TODO - throw CertificateException?792}793}794795/**796* Generates certificates from byte data and stores into cert collection.797* <p>798* This method is called by native codes in security.cpp.799*800* @param data Byte data.801* @param certCollection Collection of certificates.802*/803private void generateCertificate(byte[] data,804Collection<Certificate> certCollection) {805try {806ByteArrayInputStream bis = new ByteArrayInputStream(data);807808// Obtain certificate factory809if (certificateFactory == null) {810certificateFactory = CertificateFactory.getInstance("X.509", "SUN");811}812813// Generate certificate814Collection<? extends Certificate> c =815certificateFactory.generateCertificates(bis);816certCollection.addAll(c);817} catch (CertificateException e) {818// Ignore the exception and skip this certificate819// TODO - throw CertificateException?820} catch (Throwable te) {821// Ignore the exception and skip this certificate822// TODO - throw CertificateException?823}824}825826/**827* Returns the name of the keystore.828*/829private String getName() {830return storeName;831}832833/**834* Load keys and/or certificates from keystore into Collection.835*836* @param name Name of keystore.837*/838private native void loadKeysOrCertificateChains(String name)839throws KeyStoreException;840841/**842* Stores a DER-encoded certificate into the certificate store843*844* @param name Name of the keystore.845* @param alias Name of the certificate.846* @param encoding DER-encoded certificate.847*/848private native void storeCertificate(String name, String alias,849byte[] encoding, int encodingLength, long hCryptProvider,850long hCryptKey) throws CertificateException, KeyStoreException;851852/**853* Removes the certificate from the certificate store854*855* @param name Name of the keystore.856* @param alias Name of the certificate.857* @param encoding DER-encoded certificate.858*/859private native void removeCertificate(String name, String alias,860byte[] encoding, int encodingLength)861throws CertificateException, KeyStoreException;862863/**864* Destroys the key container.865*866* @param keyContainerName The name of the key container.867*/868private native void destroyKeyContainer(String keyContainerName)869throws KeyStoreException;870871/**872* Generates a private-key BLOB from a key's components.873*/874private native byte[] generateRSAPrivateKeyBlob(875int keyBitLength,876byte[] modulus,877byte[] publicExponent,878byte[] privateExponent,879byte[] primeP,880byte[] primeQ,881byte[] exponentP,882byte[] exponentQ,883byte[] crtCoefficient) throws InvalidKeyException;884885private native CPrivateKey storePrivateKey(String alg, byte[] keyBlob,886String keyContainerName, int keySize) throws KeyStoreException;887}888889890