TrustedSubject.java

/*
 * Copyright 2026 Emmanuel Bourg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.jsign.asn1.authenticode;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;

/**
 * Trusted subject of a Certificate Trust List.
 *
 * <pre>
 * TrustedSubject ::= SEQUENCE {
 *     subjectIdentifier    OCTET STRING,
 *     attributes           SET OF Attribute OPTIONAL
 * }
 *
 * Attribute ::= SEQUENCE {
 *     type                 OBJECT IDENTIFIER,
 *     values               SET OF ANY
 * }
 * </pre>
 *
 * @since 8.0
 */
public class TrustedSubject {

    public static final ASN1ObjectIdentifier CERT_MD5_HASH_PROP_ID                      = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.4");
    public static final ASN1ObjectIdentifier CERT_ENHKEY_USAGE_PROP_ID	                = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.9");
    public static final ASN1ObjectIdentifier CERT_FRIENDLY_NAME_PROP_ID                 = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.11");
    public static final ASN1ObjectIdentifier CERT_SIGNATURE_HASH_PROP_ID                = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.15");
    public static final ASN1ObjectIdentifier CERT_KEY_IDENTIFIER_PROP_ID                = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.20");
    public static final ASN1ObjectIdentifier CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.28");
    public static final ASN1ObjectIdentifier CERT_SUBJECT_NAME_MD5_HASH_PROP_ID         = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.29");
    public static final ASN1ObjectIdentifier CERT_ROOT_PROGRAM_CERT_POLICIES_PROP_ID    = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.83");
    public static final ASN1ObjectIdentifier CERT_DISALLOWED_FILETIME_PROP_ID           = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.11.104");

    /** The SHA-1 thumbprint of the certificate. */
    private final byte[] subjectIdentifier;

    /** Certificate metadata (enhanced key usage, friendly name, etc.) */
    private final List<Attribute> attributes = new ArrayList<>();

    public TrustedSubject(ASN1Encodable encodable) {
        ASN1Sequence sequence = ASN1Sequence.getInstance(encodable);
        subjectIdentifier = ASN1OctetString.getInstance(sequence.getObjectAt(0)).getOctets();

        if (sequence.size() > 1) {
            ASN1Set attributeSet = ASN1Set.getInstance(sequence.getObjectAt(1));
            for (ASN1Encodable attribute : attributeSet) {
                attributes.add(Attribute.getInstance(attribute));
            }
        }
    }

    public byte[] getSubjectIdentifier() {
        return subjectIdentifier;
    }

    public List<Attribute> getAttributes() {
        return attributes;
    }

    public ASN1Set getAttributeValues(ASN1ObjectIdentifier oid) {
        for (Attribute attribute : attributes) {
            if (attribute.getAttrType().equals(oid)) {
                return attribute.getAttrValues();
            }
        }
        return null;
    }

    public String getFriendlyName() {
        ASN1Set values = getAttributeValues(CERT_FRIENDLY_NAME_PROP_ID);
        if (values == null) {
            return null;
        }

        ASN1OctetString octets = (ASN1OctetString) values.getObjectAt(0);
        return new String(octets.getOctets(), StandardCharsets.UTF_16LE).trim();
    }

    public ExtendedKeyUsage getEnhancedKeyUsage() {
        ASN1Set values = getAttributeValues(CERT_ENHKEY_USAGE_PROP_ID);
        if (values == null) {
            return null;
        }

        ASN1OctetString octets = (ASN1OctetString) values.getObjectAt(0);
        return ExtendedKeyUsage.getInstance(octets.getOctets());
    }
}