org.codice.ddf.condition.SignerCondition.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.condition.SignerCondition.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program 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 Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.condition;

import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.osgi.framework.Bundle;
import org.osgi.service.condpermadmin.Condition;
import org.osgi.service.condpermadmin.ConditionInfo;

/**
 * The interface implemented by a Condition. Conditions are bound to Permissions using Conditional
 * Permission Info. The Permissions of a ConditionalPermission Info can only be used if the
 * associated Conditions are satisfied.
 *
 * <p>Selects bundles based on the identity of the signer. @ThreadSafe
 */
public final class SignerCondition extends AbstractCondition implements Condition {

    private Bundle bundle;
    private String[] args;

    public SignerCondition(Bundle bundle, ConditionInfo conditionInfo) {
        this.bundle = bundle;
        args = conditionInfo.getArgs();
    }

    /**
     * Returns whether the Condition is satisfied. This method is only called for immediate Condition
     * objects or immutable postponed conditions, and must always be called inside a permission check.
     * Mutable postponed Condition objects will be called with the grouped version {@link
     * #isSatisfied(Condition[],Dictionary)} at the end of the permission check.
     *
     * @return {@code true} to indicate the Conditions is satisfied. Otherwise, {@code false} if the
     *     Condition is not satisfied.
     */
    @Override
    public boolean isSatisfied() {
        Map<X509Certificate, List<X509Certificate>> signerCertificates = AccessController
                .doPrivileged((PrivilegedAction<Map<X509Certificate, List<X509Certificate>>>) () -> bundle
                        .getSignerCertificates(Bundle.SIGNERS_TRUSTED));
        Set<X509Certificate> x509Certificates = signerCertificates.keySet();
        for (String arg : args) {
            boolean satisfied = false;
            for (X509Certificate x509Certificate : x509Certificates) {
                String cn = getExtendedCertAttribute(x509Certificate.getSubjectX500Principal(), BCStyle.CN);
                List<String> subjectAlternativeNames = getSubjectAlternativeNames(x509Certificate);
                if (arg.equals(cn) || subjectAlternativeNames.contains(arg)) {
                    satisfied = true;
                }
            }
            if (!satisfied) {
                return false;
            }
        }
        return true;
    }

    private static String getExtendedCertAttribute(X500Principal principal, ASN1ObjectIdentifier identifier) {
        RDN[] rdNs = new X500Name(principal.getName()).getRDNs(identifier);
        if (rdNs != null && rdNs.length > 0) {
            AttributeTypeAndValue attributeTypeAndValue = rdNs[0].getFirst();
            if (attributeTypeAndValue != null) {
                return attributeTypeAndValue.getValue().toString();
            }
        }
        return "";
    }

    private List<String> getSubjectAlternativeNames(X509Certificate certificate) {
        List<String> identities = new ArrayList<>();
        try {
            Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
            if (altNames == null) {
                return Collections.emptyList();
            }
            collectAltNames(altNames, identities);
        } catch (CertificateParsingException e) {
            return Collections.emptyList();
        }
        return identities;
    }

    private void collectAltNames(Collection<List<?>> altNames, List<String> identities) {
        for (List item : altNames) {
            Integer type = (Integer) item.get(0);
            if (type == 0 || type == 2) {
                try {
                    Object[] objects = item.toArray();
                    if (objects[1] instanceof byte[]) {
                        identities.add(getIdentifyFromBytes((byte[]) objects[1]));
                    } else if (objects[1] instanceof String) {
                        identities.add((String) objects[1]);
                    }
                } catch (RuntimeException e) {
                    return;
                }
            }
        }
    }

    // this code was grabbed online and should be correct
    // we don't have any tests for this so be wary of changing this code
    private String getIdentifyFromBytes(byte[] itemBytes) {
        try (ASN1InputStream decoder = new ASN1InputStream(itemBytes)) {
            ASN1Encodable encoded = decoder.readObject();
            encoded = ((DERSequence) encoded).getObjectAt(1);
            encoded = ((DERTaggedObject) encoded).getObject();
            encoded = ((DERTaggedObject) encoded).getObject();
            return ((DERUTF8String) encoded).getString();
        } catch (IOException e) {
            return "";
        }
    }
}