GraniteKeyStoreSpi.java 10.5 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Binary
 *  javax.jcr.Node
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.ValueFactory
 *  org.apache.sling.api.resource.Resource
 *  org.apache.sling.api.resource.ResourceResolver
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.keystore.internal;

import com.adobe.granite.crypto.CryptoSupport;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Date;
import java.util.Enumeration;
import javax.jcr.Binary;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GraniteKeyStoreSpi
extends KeyStoreSpi {
    private static final Logger LOG = LoggerFactory.getLogger(GraniteKeyStoreSpi.class);
    private final Resource resource;
    private final Resource contentResource;
    private final KeyStore keyStore;
    private final String encryptedPassword;
    private final CryptoSupport cryptoSupport;
    private final boolean isIBM;

    public GraniteKeyStoreSpi(Resource resource, KeyStore keyStore, String encryptedPassword, CryptoSupport cryptoSupport) {
        this.resource = resource;
        this.keyStore = keyStore;
        this.encryptedPassword = encryptedPassword;
        this.cryptoSupport = cryptoSupport;
        this.contentResource = resource.getChild("jcr:content");
        if (this.contentResource == null) {
            throw new IllegalArgumentException("Expected a jcr:content child for resource " + resource.getPath());
        }
        if (!"nt:resource".equals(this.contentResource.getResourceType())) {
            throw new IllegalArgumentException("Expected a jcr:content child of type nt:resource for resource " + resource.getPath());
        }
        boolean ibm = false;
        for (Provider p : Security.getProviders()) {
            if (!p.getName().toLowerCase().contains("ibm")) continue;
            ibm = true;
            break;
        }
        this.isIBM = ibm;
    }

    @Override
    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
        try {
            return this.keyStore.getKey(alias, password);
        }
        catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public Certificate[] engineGetCertificateChain(String alias) {
        try {
            return this.keyStore.getCertificateChain(alias);
        }
        catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public Certificate engineGetCertificate(String alias) {
        try {
            return this.keyStore.getCertificate(alias);
        }
        catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public Date engineGetCreationDate(String alias) {
        try {
            return this.keyStore.getCreationDate(alias);
        }
        catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException {
        if (this.isIBM) {
            this.ibmKeyIntegrityCheck(alias, key, null, chain, true);
        }
        this.keyStore.setKeyEntry(alias, key, password, chain);
        this.persist();
    }

    @Override
    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException {
        if (this.isIBM) {
            this.ibmKeyIntegrityCheck(alias, null, key, chain, false);
        }
        this.keyStore.setKeyEntry(alias, key, chain);
        this.persist();
    }

    @Override
    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
        this.keyStore.setCertificateEntry(alias, cert);
        this.persist();
    }

    @Override
    public void engineDeleteEntry(String alias) throws KeyStoreException {
        this.keyStore.deleteEntry(alias);
        this.persist();
    }

    @Override
    public Enumeration<String> engineAliases() {
        try {
            return this.keyStore.aliases();
        }
        catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public boolean engineContainsAlias(String alias) {
        try {
            return this.keyStore.containsAlias(alias);
        }
        catch (KeyStoreException e) {
            return false;
        }
    }

    @Override
    public int engineSize() {
        try {
            return this.keyStore.size();
        }
        catch (KeyStoreException e) {
            return 0;
        }
    }

    @Override
    public boolean engineIsKeyEntry(String alias) {
        try {
            return this.keyStore.isKeyEntry(alias);
        }
        catch (KeyStoreException e) {
            return false;
        }
    }

    @Override
    public boolean engineIsCertificateEntry(String alias) {
        try {
            return this.keyStore.isCertificateEntry(alias);
        }
        catch (KeyStoreException e) {
            return false;
        }
    }

    @Override
    public String engineGetCertificateAlias(Certificate cert) {
        try {
            return this.keyStore.getCertificateAlias(cert);
        }
        catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {
        throw new IOException("Persistence is handled automatically by the SPI.");
    }

    @Override
    public void engineStore(KeyStore.LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException {
        throw new IOException("Persistence is handled automatically by the SPI.");
    }

    @Override
    public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {
        if (stream != null) {
            throw new IOException("The SPI handles persistence on its own, being backed-up by the resource with which it was initialized. Please call this method with a null stream parameter.");
        }
        Node storeNode = (Node)this.contentResource.adaptTo(Node.class);
        try {
            InputStream is = storeNode.getProperty("jcr:data").getBinary().getStream();
            this.keyStore.load(is, password);
        }
        catch (RepositoryException e) {
            LOG.error("Cannot access the jcr:data property on the jcr:content node of the keystore resource.", (Throwable)e);
            throw new IOException((Throwable)e);
        }
    }

    @Override
    public void engineLoad(KeyStore.LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException {
        this.keyStore.load(param);
    }

    @Override
    public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
        if (this.keyStore.isCertificateEntry(alias)) {
            return new KeyStore.TrustedCertificateEntry(this.keyStore.getCertificate(alias));
        }
        if (this.keyStore.isKeyEntry(alias)) {
            return this.keyStore.getEntry(alias, protParam);
        }
        return this.keyStore.getEntry(alias, protParam);
    }

    @Override
    public void engineSetEntry(String alias, KeyStore.Entry entry, KeyStore.ProtectionParameter protParam) throws KeyStoreException {
        this.keyStore.setEntry(alias, entry, protParam);
        this.persist();
    }

    @Override
    public boolean engineEntryInstanceOf(String alias, Class<? extends KeyStore.Entry> entryClass) {
        try {
            return this.keyStore.entryInstanceOf(alias, entryClass);
        }
        catch (KeyStoreException e) {
            return false;
        }
    }

    private void persist() throws KeyStoreException {
        block4 : {
            try {
                if (this.contentResource.getResourceResolver().isLive()) {
                    Node storeNode = (Node)this.contentResource.adaptTo(Node.class);
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    this.keyStore.store(os, this.cryptoSupport.unprotect(this.encryptedPassword).toCharArray());
                    ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
                    Session session = storeNode.getSession();
                    storeNode.setProperty("jcr:data", session.getValueFactory().createBinary((InputStream)is));
                    is.close();
                    os.close();
                    session.save();
                    break block4;
                }
                throw new IOException("The resource resolver used to access resource " + this.resource.getPath() + " was closed.");
            }
            catch (Exception e) {
                if (e instanceof KeyStoreException) {
                    throw (KeyStoreException)e;
                }
                throw new KeyStoreException(e);
            }
        }
    }

    private void ibmKeyIntegrityCheck(String alias, Key key, byte[] keyEncoded, Certificate[] chain, boolean passwordProtectedEntry) throws KeyStoreException {
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12", "AdobeGraniteSecurityProvider");
            keyStore.load(null, null);
            if (passwordProtectedEntry) {
                keyStore.setKeyEntry(alias, key, this.cryptoSupport.unprotect(this.encryptedPassword).toCharArray(), chain);
            } else {
                keyStore.setKeyEntry(alias, keyEncoded, chain);
            }
            keyStore.getEntry(alias, new KeyStore.PasswordProtection(this.cryptoSupport.unprotect(this.encryptedPassword).toCharArray()));
        }
        catch (Exception e) {
            throw new KeyStoreException(e);
        }
    }
}