sun.security.mscapi.KeyStoreAddressBook Maven / Gradle / Ivy
Show all versions of afirma-keystores-capiaddressbook Show documentation
/*
* Copyright (c) 2005, 2006, 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.SecurityPermission;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Logger;
/** sun.security.mscapi.KeyStore
modificada para acceder a los
* almacenes de CAPI ADDRESSBOOK y CA.
* @author Tomás García-Merás */
public abstract class KeyStoreAddressBook extends KeyStoreSpi {
/** KeyStore CA de CAPI. */
public static final class CA extends KeyStoreAddressBook {
/** Construye el SPI
del KeyStore CA de CAPI. */
public CA() {
super("CA"); //$NON-NLS-1$
}
}
/** KeyStore ADDRESSBOOK de CAPI. */
public static final class ADDRESSBOOK extends KeyStoreAddressBook {
/** Construye el SPI
del KeyStore ADDRESSBOOK de
* CAPI. */
public ADDRESSBOOK() {
super("ADDRESSBOOK"); //$NON-NLS-1$
}
}
final class KeyEntry {
private final X509Certificate certChain[];
private String alias;
X509Certificate[] getCertChain() {
return this.certChain.clone();
}
KeyEntry(final X509Certificate[] chain) {
this((String) null, chain);
}
KeyEntry(final String alias, final X509Certificate[] chain) {
this.certChain = chain.clone();
/*
* 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(this.certChain[0].hashCode());
}
else {
this.alias = alias;
}
}
/** Gets the alias for the keystore entry.
* @return Alias para la entrada de almacén. */
String getAlias() {
return this.alias;
}
/** Gets the certificate chain for the keystore entry.
* @return cadena de certificados para la entrada de almacén. */
X509Certificate[] getCertificateChain() {
return this.certChain;
}
}
/** The keystore entries. */
private final Collection entries = new ArrayList<>();
/** The keystore name. Case is not significant. */
private final String storeName;
private java.lang.reflect.Method loadKeysOrCertificateChains;
private final Object nativeWrapper;
KeyStoreAddressBook(final String storeName) {
try {
final Class> keyStoreMyClass = Class.forName("sun.security.mscapi.KeyStore$MY"); //$NON-NLS-1$
// Esto equivale a {@code new KeyStore.MY()}.
this.nativeWrapper = keyStoreMyClass.getConstructor().newInstance();
}
catch (final Exception e) {
Logger.getLogger("es.gob.afirma").severe("No se han encontrado las clases de SunMSCapi: " + e); //$NON-NLS-1$ //$NON-NLS-2$
throw new RuntimeException("No se han encontrado las clases de SunMSCapi", e); //$NON-NLS-1$
}
try {
this.nativeWrapper.getClass();
for (final java.lang.reflect.Method m : this.nativeWrapper.getClass().getDeclaredMethods()) {
m.setAccessible(true);
}
for (final java.lang.reflect.Method m : this.nativeWrapper.getClass().getSuperclass().getDeclaredMethods()) {
m.setAccessible(true);
if (m.getName().equals("loadKeysOrCertificateChains")) { //$NON-NLS-1$
this.loadKeysOrCertificateChains = m;
}
}
}
catch (final Exception e) {
Logger.getLogger("es.gob.afirma").severe("No se han podido obtener los metodos de acceso a sunmscapi.dll: " + e); //$NON-NLS-1$ //$NON-NLS-2$
}
this.storeName = storeName;
}
/** Returns the key associated with the given alias.
*
* A compatibility mode is supported for applications that assume a password must be supplied. It permits (but ignores) a non-null
* password
. The mode is enabled by default. Set the sun.security.mscapi.keyStoreCompatibilityMode
system property to
* false
to disable compatibility mode and reject a non-null password
.
* @param alias
* the alias name
* @param password
* the password, which should be null
* @return the requested key, or null if the given alias does not exist or
* does not identify a key entry. */
@Override
public final java.security.Key engineGetKey(final String alias, final char[] password) {
throw new UnsupportedOperationException();
}
/** 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 trusted certificate
* entry or a key entry without a certificate chain). */
@Override
public final Certificate[] engineGetCertificateChain(final String alias) {
if (alias == null) {
return null;
}
// Se usan los KeyEntry por reflexion porque se han detectado casos en los que son del
// tipo del almacen de windows en lugar de la libreta de direcciones
try {
for (final Object entry : this.entries.toArray()) {
final Method getAliasMethod = entry.getClass().getDeclaredMethod("getAlias"); //$NON-NLS-1$
getAliasMethod.setAccessible(true);
if (alias.equals(getAliasMethod.invoke(entry))) {
final Method getCertificateChainMethod = entry.getClass().getDeclaredMethod("getCertificateChain"); //$NON-NLS-1$
getCertificateChainMethod.setAccessible(true);
return (Certificate[]) getCertificateChainMethod.invoke(entry);
}
}
}
catch (final Exception e) {
Logger.getLogger("es.gob.afirma").warning("Error tratando de obtener la cadena de certificacion: " + e); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
/** Returns the certificate associated with the given alias.
*
* If the given alias name identifies a trusted certificate entry, the certificate associated with that entry is returned. If the given
* alias name identifies a key entry, 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. */
@Override
public final Certificate engineGetCertificate(final String alias) {
if (alias == null) {
return null;
}
java.lang.reflect.Method getAlias = null;
java.lang.reflect.Method getCertificateChain = null;
for (final Object o : this.entries) {
for (final java.lang.reflect.Method m : o.getClass().getDeclaredMethods()) {
if (m.getName().equals("getAlias")) { //$NON-NLS-1$
m.setAccessible(true);
getAlias = m;
}
else if (m.getName().equals("getCertificateChain")) { //$NON-NLS-1$
m.setAccessible(true);
getCertificateChain = m;
}
if (getAlias != null) {
try {
if (alias.equals(getAlias.invoke(o, new Object[0])) && getCertificateChain != null) {
final X509Certificate[] certChain = (X509Certificate[]) getCertificateChain.invoke(o, new Object[0]);
return certChain[0];
}
}
catch (final Exception e) {
Logger.getLogger("es.gob.afirma").warning("Error obteniendo el certificado para el alias '" + alias + "', se devolvera null: " + e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
}
return null;
}
/** 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 */
@Override
public final Date engineGetCreationDate(final String alias) {
if (alias == null) {
return null;
}
return new Date();
}
/** Stores the given private key and associated certificate chain in the
* keystore.
*
* The given java.security.PrivateKey key
must be accompanied by a certificate chain certifying the corresponding public key.
*
* 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.
*
* A compatibility mode is supported for applications that assume a password must be supplied. It permits (but ignores) a non-null
* password
. The mode is enabled by default. Set the sun.security.mscapi.keyStoreCompatibilityMode
system property to
* false
to disable compatibility mode and reject a non-null password
.
* @param alias
* the alias name
* @param key
* the private key to be associated with the alias
* @param password
* the password, which should be null
* @param chain
* the certificate chain for the corresponding public key (only
* required if the given key is of type java.security.PrivateKey
). */
@Override
public final void engineSetKeyEntry(final String alias, final java.security.Key key, final char[] password, final Certificate[] chain) {
throw new UnsupportedOperationException();
}
/** Assigns the given key (that has already been protected) to the given
* alias.
*
* If the protected key is of type java.security.PrivateKey
, it must be accompanied by a certificate chain certifying the
* corresponding public key. If the underlying keystore implementation is of type jks
, key
must be encoded as an
* EncryptedPrivateKeyInfo
as defined in the PKCS #8 standard.
*
* 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 java.security.PrivateKey
). */
@Override
public final void engineSetKeyEntry(final String alias, final byte[] key, final Certificate[] chain) {
throw new UnsupportedOperationException("Cannot assign the encoded key to the given alias."); //$NON-NLS-1$
}
/** Assigns the given certificate to the given alias.
*
* If the given alias already exists in this keystore and identifies a trusted certificate entry, the certificate associated with it is
* overridden by the given certificate.
* @param alias
* the alias name
* @param cert
* the certificate. */
@Override
public final void engineSetCertificateEntry(final String alias, final Certificate cert) {
throw new UnsupportedOperationException();
}
/** Deletes the entry identified by the given alias from this keystore.
* @param alias
* the alias name. */
@Override
public final void engineDeleteEntry(final String alias) {
throw new UnsupportedOperationException();
}
/** Lists all the alias names of this keystore.
* @return enumeration of the alias names */
@Override
public final Enumeration engineAliases() {
final Iterator iter = this.entries.iterator();
return new Enumeration() {
/** {@inheritDoc} */
@Override
public boolean hasMoreElements() {
return iter.hasNext();
}
/** {@inheritDoc} */
@Override
public String nextElement() {
final Object o = iter.next();
for (final java.lang.reflect.Method m : o.getClass().getDeclaredMethods()) {
if (m.getName().equals("getAlias")) { //$NON-NLS-1$
m.setAccessible(true);
try {
return m.invoke(o, new Object[0]).toString();
}
catch (final Exception e) {
Logger.getLogger("es.gob.afirma").severe("No se ha podido invocar a sunmscapi.dll para obtener los alias: " + e); //$NON-NLS-1$//$NON-NLS-2$
return null;
}
}
}
return null;
}
};
}
/** Checks if the given alias exists in this keystore.
* @param alias
* the alias name
* @return true if the alias exists, false otherwise */
@Override
public final boolean engineContainsAlias(final String alias) {
for (final Enumeration> enumerator = engineAliases(); enumerator.hasMoreElements();) {
final String a = (String) enumerator.nextElement();
if (a.equals(alias)) {
return true;
}
}
return false;
}
/** Retrieves the number of entries in this keystore.
* @return the number of entries in this keystore */
@Override
public final int engineSize() {
return this.entries.size();
}
/** Returns true if the entry identified by the given alias is a key
* entry, and false otherwise.
* @return true if the entry identified by the given alias is a key
* entry, false otherwise. */
@Override
public final boolean engineIsKeyEntry(final String alias) {
throw new UnsupportedOperationException();
}
/** Returns true if the entry identified by the given alias is a trusted
* certificate entry, and false otherwise.
* @return true if the entry identified by the given alias is a trusted
* certificate entry, false otherwise. */
@Override
public final boolean engineIsCertificateEntry(final String alias) {
throw new UnsupportedOperationException();
}
/** Returns the (alias) name of the first keystore entry whose certificate
* matches the given certificate.
*
* This method attempts to match the given certificate with each keystore entry. If the entry being considered is a trusted certificate
* entry, the given certificate is compared to that entry's certificate. If the entry being considered is a key entry, 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. */
@Override
public final String engineGetCertificateAlias(final Certificate cert) {
for (final KeyEntry entry : this.entries) {
if (entry.getCertChain() != null && entry.getCertChain()[0].equals(cert)) {
return entry.getAlias();
}
}
return null;
}
/** engineStore is currently a no-op. Entries are stored during
* engineSetEntry.
* @param stream
* the output stream, which should be null
* @param password
* the password, which should be null
. */
@Override
public final void engineStore(final OutputStream stream, final char[] password) {
// No es necesario hacer nada, se almacena en engineSetEntry()
}
/** Loads the keystore.
* A compatibility mode is supported for applications that assume keystores
* are stream-based. It permits (but ignores) a non-null stream
or password
. The mode is enabled by default. Set the
* sun.security.mscapi.keyStoreCompatibilityMode
system
* property to false
to disable compatibility mode and reject a
* non-null stream
or password
.
* @param stream
* the input stream, which should be null
.
* @param password
* the password, which should be null
.
* @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 SecurityException
* if the security check for SecurityPermission("authProvider.name")
does not pass, where name is the value
* returned by
* this provider's getName
method. */
@Override
public final void engineLoad(final InputStream stream, final char[] password) throws IOException {
/*
* Use the same security check as AuthProvider.login
*/
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SecurityPermission("authProvider.SunMSCAPI")); //$NON-NLS-1$
}
// Clear all key entries
this.entries.clear();
try {
// Load keys and/or certificate chains
loadKeysOrCertificateChains(getName(), this.entries);
}
catch (final KeyStoreException kse) {
// Wrap the JNI exception in an IOException
throw new IOException(kse.toString(), kse);
}
}
/** Devuelve el nombre del almacén.
* @return Nombre del almacén. */
private String getName() {
return this.storeName;
}
/** Load keys and/or certificates from keystore into Collection.
* @param name Name of keystore.
* @param ntries Collection of key/certificate.
* @throws KeyStoreException Si hay problemas tratando el almacén. */
private void loadKeysOrCertificateChains(final String name, final Collection ntries) throws KeyStoreException {
try {
// Los ultimos MSCapi no incluyen este metodo, asi que en caso de error
// acudimos a un modo alternativo
this.loadKeysOrCertificateChains.invoke(this.nativeWrapper, name, ntries);
}
catch (final Exception e) {
try {
// Cargamos las entradas, accedemos al campo que las contiene y componemos
// el objeto resultado con sus valores
this.loadKeysOrCertificateChains.invoke(this.nativeWrapper, name);
final java.lang.reflect.Field entriesField = this.nativeWrapper.getClass().
getSuperclass().getDeclaredField("entries"); //$NON-NLS-1$
entriesField.setAccessible(true);
final HashMap coll = (HashMap) entriesField.get(this.nativeWrapper);
final Iterator it = coll.keySet().iterator();
while (it.hasNext()) {
final String alias = it.next();
final Object keyEntryObject = coll.get(alias);
final Method getCertificateChainMethod = keyEntryObject.getClass().getDeclaredMethod("getCertificateChain"); //$NON-NLS-1$
getCertificateChainMethod.setAccessible(true);
ntries.add(new KeyEntry(alias, (X509Certificate[]) getCertificateChainMethod.invoke(keyEntryObject)));
}
}
catch (final Exception e2) {
throw new KeyStoreException(e);
}
}
}
}