All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.signatures.validation.extensions.KeyUsageExtension Maven / Gradle / Ivy

The newest version!
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2025 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.signatures.validation.extensions;

import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.io.util.EnumUtil;
import com.itextpdf.kernel.crypto.OID;

import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;

/**
 * Class representing "Key Usage" extenstion.
 */
public class KeyUsageExtension extends CertificateExtension {

    private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();
    public static final String EXPECTED_VALUE =
            "Key usage expected: ({0})";
    public static final String ACTUAL_VALUE = "\nbut found {0}";
    public static final String MISSING_VALUE = "\nbut nothing found.";

    private final int keyUsage;
    private final boolean resultOnMissingExtension;
    private String messagePreAmble;
    private String message;

    /**
     * Create new {@link KeyUsageExtension} instance using provided {@code int} flag.
     *
     * @param keyUsage {@code int} flag which represents bit values for key usage value
     *                             bit strings are stored with the big-endian byte order and padding on the end,
     *                             the big endian notation causes a shift in actual integer values for
     *                             bits 1-8 becoming 0-7 and bit 1
     *                             the 7 bits padding makes for bit 0 to become bit 7 of the first byte
     */
    public KeyUsageExtension(int keyUsage) {
        this(keyUsage, false);
    }

    /**
     * Create new {@link KeyUsageExtension} instance using provided {@code int} flag.
     *
     * @param keyUsage                 {@code int} flag which represents bit values for key usage value
     *                                 bit strings are stored with the big-endian byte order and padding on the end,
     *                                 the big endian notation causes a shift in actual integer values for bits 1-8
     *                                 becoming 0-7 and bit 1
     *                                 the 7 bits padding makes for bit 0 to become bit 7 of the first byte
     * @param resultOnMissingExtension parameter which represents return value for
     * {@link #existsInCertificate(X509Certificate)} method in case of the extension not being present in a certificate
     */
    public KeyUsageExtension(int keyUsage, boolean resultOnMissingExtension) {
        super(OID.X509Extensions.KEY_USAGE, FACTORY.createKeyUsage(keyUsage).toASN1Primitive());
        this.keyUsage = keyUsage;
        this.resultOnMissingExtension = resultOnMissingExtension;
        messagePreAmble =MessageFormatUtil.format(EXPECTED_VALUE , convertKeyUsageMaskToString(keyUsage));
        message = messagePreAmble;
    }

    /**
     * Create new {@link KeyUsageExtension} instance using provided key usage enum list.
     *
     * @param keyUsages key usages {@link List} which represents key usage values
     */
    public KeyUsageExtension(List keyUsages) {
        this(keyUsages, false);
    }

    /**
     * Create new {@link KeyUsageExtension} instance using provided key usage enum list.
     *
     * @param keyUsages                key usages {@link List} which represents key usage values
     * @param resultOnMissingExtension parameter which represents return value for
     * {@link #existsInCertificate(X509Certificate)} method in case of the extension not being present in a certificate
     */
    public KeyUsageExtension(List keyUsages, boolean resultOnMissingExtension) {
        this(convertKeyUsageSetToInt(keyUsages), resultOnMissingExtension);
    }

    /**
     * Create new {@link KeyUsageExtension} instance using provided single key usage enum value.
     *
     * @param keyUsageValue {@link KeyUsage} which represents single key usage enum value
     */
    public KeyUsageExtension(KeyUsage keyUsageValue) {
        this(Collections.singletonList(keyUsageValue), false);
    }

    /**
     * Create new {@link KeyUsageExtension} instance using provided single key usage enum value.
     *
     * @param keyUsageValue {@link KeyUsage} which represents single key usage enum value
     * @param resultOnMissingExtension parameter which represents return value for
     * {@link #existsInCertificate(X509Certificate)} method in case of the extension not being present in a certificate
     */
    public KeyUsageExtension(KeyUsage keyUsageValue, boolean resultOnMissingExtension) {
        this(Collections.singletonList(keyUsageValue), resultOnMissingExtension);
    }

    /**
     * Check if this extension is present in the provided certificate. In case of {@link KeyUsageExtension},
     * check if this key usage bit values are present in certificate. Other values may be present as well.
     *
     * @param certificate {@link X509Certificate} in which this extension shall be present
     *
     * @return {@code true} if this key usage bit values are present in certificate, {@code false} otherwise
     */
    @Override
    public boolean existsInCertificate(X509Certificate certificate) {
        boolean[] providedKeyUsageFlags = certificate.getKeyUsage();
        if (providedKeyUsageFlags == null) {
            // By default, we want to return true if extension is not specified. Configurable.
            message = messagePreAmble + MISSING_VALUE;
            return resultOnMissingExtension;
        }
        int bitmap = 0;
        // bit strings are stored with the big-endian byte order and padding on the end,
        // the big endian notation causes a shift in actual integer values for bits 1-8 becoming 0-7 and bit 1
        // the 7 bits padding makes for bit 0 to become bit 7 of the first byte
        for (int i = 0; i < providedKeyUsageFlags.length - 1; ++i) {
            if (providedKeyUsageFlags[i]) {
                bitmap +=  1 << (8-i-1);
            }
        }
        if (providedKeyUsageFlags[8]) {
            bitmap +=  0x8000;
        }
        if ((bitmap & keyUsage) != keyUsage) {
            message = new StringBuilder(messagePreAmble).append(
                    MessageFormatUtil.format(ACTUAL_VALUE, convertKeyUsageMaskToString(bitmap)))
                    .toString();
            return false;
        }
        return true;
    }
    @Override
    public String getMessage() {
        return message;
    }

    private static String convertKeyUsageMaskToString(int keyUsageMask) {
        StringBuilder result = new StringBuilder();
        String separator = "";
        // bit strings are stored with the big-endian byte order and padding on the end,
        // the big endian notation causes a shift in actual integer values for bits 1-8 becoming 0-7 and bit 1
        // the 7 bits padding makes for bit 0 to become bit 7 of the first byte
        for (KeyUsage usage: EnumUtil.getAllValuesOfEnum(KeyUsage.class)) {
            if (((1 << (8-usage.ordinal()-1)) & keyUsageMask) > 0 ||
                    (usage == KeyUsage.DECIPHER_ONLY &&  (keyUsageMask & 0x8000) == 0x8000)) {
                result.append(separator)
                        .append(usage);
                separator = ", ";
            }
        }
        return result.toString();
    }
    private static int convertKeyUsageSetToInt(Iterable keyUsages) {
        int keyUsageMask = 0;
        // bit strings are stored with the big-endian byte order and padding on the end,
        // the big endian notation causes a shift in actual integer values for bits 1-8 becoming 0-7 and bit 1
        // the 7 bits padding makes for bit 0 to become bit 7 of the first byte
        for (KeyUsage usage: keyUsages) {
            if (usage == KeyUsage.DECIPHER_ONLY) {
                keyUsageMask += 0x8000;
                continue;
            }
            keyUsageMask +=  1 << ( 8 - usage.ordinal() - 1);
        }
        return keyUsageMask;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy