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

org.wildfly.security.sasl.entity.EntityUtil Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 org.wildfly.security.sasl.entity;

import static org.wildfly.security.asn1.ASN1.*;
import static org.wildfly.security.mechanism._private.ElytronMessages.saslEntity;
import static org.wildfly.security.sasl.entity.Entity.*;
import static org.wildfly.security.x500.GeneralName.*;
import static org.wildfly.security.x500.TrustedAuthority.*;

import java.io.ByteArrayInputStream;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

import javax.security.auth.x500.X500Principal;

import org.wildfly.security.asn1.ASN1Exception;
import org.wildfly.security.asn1.DERDecoder;
import org.wildfly.security.asn1.DEREncoder;
import org.wildfly.security.x500.GeneralName;
import org.wildfly.security.x500.TrustedAuthority;
import org.wildfly.security.x500.X500;
import org.wildfly.security.x500.util.X500PrincipalUtil;

/**
 * @author Farah Juma
 */
class EntityUtil {

    private static final byte[] randomCharDictionary;

    static {
        byte[] dict = new byte[93];
        int i = 0;
        for (byte c = '!'; c < ','; c ++) {
            dict[i ++] = c;
        }
        for (byte c = ',' + 1; c < 127; c ++) {
            dict[i ++] = c;
        }
        assert i == dict.length;
        randomCharDictionary = dict;
    }

    /* -- Methods used to encode ASN.1 data structures required for entity authentication -- */

    /**
     * Encode an ASN.1 set of certificates using the given DER encoder and the
     * given {@code X509Certificate} chain.
     *
     * @param encoder the DER encoder
     * @param certChain the X.509 certificate chain to encode
     * @throws ASN1Exception if an error occurs while encoding the given certificate chain
     */
    public static void encodeX509CertificateChain(final DEREncoder encoder, X509Certificate[] certChain) throws ASN1Exception {
        try {
            int chainSize = certChain.length;
            encoder.startSetOf();
            for (int i = 0; i < chainSize; i++) {
                encoder.writeEncoded(certChain[i].getEncoded());
            }
            encoder.endSetOf();
        } catch (CertificateEncodingException e) {
            throw new ASN1Exception(e);
        }
    }

    /**
     * 

* Encode an {@code AlgorithmIdentifier} without any parameters using the given * DER encoder and object identifier, where {@code AlgorithmIdentifier} is defined as: * *

     *      AlgorithmIdentifier  ::=  SEQUENCE  {
     *          algorithm      OBJECT IDENTIFIER,
     *          parameters     ANY DEFINED BY algorithm OPTIONAL
     *      }
     * 
*

* * @param encoder the DER encoder * @param objectIdentifier the object identifier for the algorithm * @param omitParametersField {@code true} if the parameters field should be ommitted in * the encoding and {@code false} otherwise * @throws ASN1Exception if the given object identifier is invalid */ public static void encodeAlgorithmIdentifier(final DEREncoder encoder, String objectIdentifier, boolean omitParametersField) throws ASN1Exception { encoder.startSequence(); encoder.encodeObjectIdentifier(objectIdentifier); if (!omitParametersField) { encoder.encodeNull(); } encoder.endSequence(); } /** *

* Encode an {@code AlgorithmIdentifier} using the given DER encoder, where * {@code AlgorithmIdentifier} is defined as: * *

     *      AlgorithmIdentifier  ::=  SEQUENCE  {
     *          algorithm      OBJECT IDENTIFIER,
     *          parameters     ANY DEFINED BY algorithm OPTIONAL
     *      }
     * 
*

* * @param encoder the DER encoder * @param algorithm the algorithm name * @throws ASN1Exception if the given algorithm name is unrecognised */ public static void encodeAlgorithmIdentifier(final DEREncoder encoder, String algorithm) throws ASN1Exception { // Determine whether or not the parameters field should be omitted in the encoding, // as specified in RFC 3279 (http://www.ietf.org/rfc/rfc3279) boolean omitParametersField; switch (algorithm) { case SHA1_WITH_RSA: { omitParametersField = false; break; } case SHA1_WITH_DSA: case SHA1_WITH_ECDSA: { omitParametersField = true; break; } default: throw saslEntity.asnUnrecognisedAlgorithm(algorithm); } encodeAlgorithmIdentifier(encoder, algorithmOid(algorithm), omitParametersField); } /** *

* Encode a {@code GeneralNames} element using the given DER encoder, where * {@code GeneralNames} is defined as: * *

     *      GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
     * 
*

* * @param encoder the DER encoder * @param generalNames the general names, as a {@code List} where each entry is a {@link GeneralName} * @throws ASN1Exception if any of the general names are invalid */ public static void encodeGeneralNames(final DEREncoder encoder, List generalNames) throws ASN1Exception { encoder.startSequence(); for (GeneralName generalName : generalNames) { generalName.encodeTo(encoder); } encoder.endSequence(); } /** * Encode a {@code GeneralNames} element consisting of one general name using * the given DER encoder. * * @param encoder the DER encoder * @param generalName the general name * @throws ASN1Exception if the general name is invalid */ public static void encodeGeneralNames(final DEREncoder encoder, GeneralName generalName) throws ASN1Exception { List generalNames = new ArrayList(1); generalNames.add(generalName); encodeGeneralNames(encoder, generalNames); } public static void encodeGeneralNames(final DEREncoder encoder, String subjectName, Collection> subjectAltNames) throws ASN1Exception { encoder.startSequence(); if (! subjectName.isEmpty()) { new DirectoryName(subjectName).encodeTo(encoder); } if (subjectAltNames != null) { for (List altName : subjectAltNames) { convertToGeneralName(altName).encodeTo(encoder); } } encoder.endSequence(); } /** *

* Encode a {@code RandomNumber} element using the given DER encoder, where * {@code RandomNumber} is defined as: * *

     *      RandomNumber ::= OCTET STRING (SIZE(8..MAX))
     * 
*

* * @param encoder the DER encoder * @param secureRandom the secure random to use (may be null) */ public static byte[] encodeRandomNumber(final DEREncoder encoder, SecureRandom secureRandom) { Random random = secureRandom != null ? secureRandom : ThreadLocalRandom.current(); byte[] randomA = generateRandomString(48, random); encoder.encodeOctetString(randomA); return randomA; } public static byte[] generateRandomString(int length, Random random) { final byte[] chars = new byte[length]; for (int i = 0; i < length; i ++) { chars[i] = randomCharDictionary[random.nextInt(93)]; } return chars; } /** * Encode an ASN.1 sequence of trusted authorities using the given DER encoder. * * @param encoder the DER encoder * @param trustedAuthorities the trusted authorities as a {@code List} where each entry must * be a {@link NameTrustedAuthority}, a {@link CertificateTrustedAuthority}, or a {@link HashTrustedAuthority} * @throws ASN1Exception if any of the trusted authorities are invalid */ public static void encodeTrustedAuthorities(final DEREncoder encoder, List trustedAuthorities) throws ASN1Exception { encoder.startSequence(); for (TrustedAuthority trustedAuthority : trustedAuthorities) { trustedAuthority.encodeTo(encoder); } encoder.endSequence(); } /* -- Methods used to decode ASN.1 data structures required for entity authentication -- */ /** * Decode the next element from the given DER decoder as a {@code GeneralNames} element. * * @param decoder the DER decoder * @return the general names * @throws ASN1Exception if the next element from the given decoder is not a general names element */ public static List decodeGeneralNames(final DERDecoder decoder) throws ASN1Exception { List generalNames = new ArrayList(); GeneralName generalName = null; decoder.startSequence(); while (decoder.hasNextElement()) { out: { for (int generalNameType = 0; generalNameType <= 8; generalNameType++) { switch (generalNameType) { case OTHER_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, true)) { decoder.decodeImplicit(generalNameType); decoder.startSequence(); String typeId = decoder.decodeObjectIdentifier(); byte[] encodedValue = decoder.drainElement(); decoder.endSequence(); generalName = new OtherName(typeId, encodedValue); break out; } break; case RFC_822_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, false)) { decoder.decodeImplicit(generalNameType); generalName = new RFC822Name(decoder.decodeIA5String()); break out; } break; case DNS_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, false)) { decoder.decodeImplicit(generalNameType); generalName = new DNSName(decoder.decodeIA5String()); break out; } break; case X400_ADDRESS: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, true)) { decoder.decodeImplicit(generalNameType); generalName = new X400Address(decoder.drainElementValue(), true); break out; } break; case DIRECTORY_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, true)) { byte[] encodedName = decoder.drainElementValue(); generalName = new DirectoryName((new X500Principal(encodedName)).getName(X500Principal.CANONICAL)); break out; } break; case EDI_PARTY_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, true)) { decoder.decodeImplicit(generalNameType); generalName = new EDIPartyName(decoder.drainElementValue(), true); break out; } break; case URI_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, false)) { decoder.decodeImplicit(generalNameType); generalName = new URIName(decoder.decodeIA5String()); break out; } break; case IP_ADDRESS: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, false)) { decoder.decodeImplicit(generalNameType); generalName = new IPAddress(decoder.decodeOctetString()); break out; } break; case REGISTERED_ID: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, generalNameType, false)) { decoder.decodeImplicit(generalNameType); generalName = new RegisteredID(decoder.decodeObjectIdentifier()); break out; } break; default: throw saslEntity.asnInvalidGeneralNameType(); } } } generalNames.add(generalName); } decoder.endSequence(); return generalNames; } /** * Decode the next element from the given DER decoder as an X.509 certificate chain. * * @param decoder the DER decoder * @return the X.509 certificate chain * @throws ASN1Exception if the next element from the given decoder is not an X.509 * certificate chain or if an error occurs while decoding the X.509 certificate chain */ public static X509Certificate[] decodeX509CertificateChain(final DERDecoder decoder) throws ASN1Exception { if (decoder.peekType() != SET_TYPE) { throw saslEntity.asnUnexpectedTag(); } byte[] certChain = decoder.drainElement(); try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); certChain[0] = SEQUENCE_TYPE; // CertificateFactory#generateCertPath requires a DER SEQUE CertPath certPath = certFactory.generateCertPath(new ByteArrayInputStream(certChain)); List certs = certPath.getCertificates(); return certs.toArray(new X509Certificate[certs.size()]); } catch (CertificateException e) { throw new ASN1Exception(e); } } /** *

* Decode the next element from the given DER decoder as a {@code CertData} element, * which is defined as follows: * *

     *      CertData ::= CHOICE {
     *          certificateSet     SET SIZE (1..MAX) OF Certificate,
     *          certURL            IA5String (Note: No support for certificate URL)
     *      }
     * 
*

* * @param decoder the DER decoder * @return the X.509 certificate or certificate chain * @throws ASN1Exception if the next element from the given decoder is not a {@code CertData} * element or if an error occurs while decoding the certificate data */ public static X509Certificate[] decodeCertificateData(final DERDecoder decoder) throws ASN1Exception { X509Certificate[] peerCertChain; if (decoder.peekType() == SET_TYPE) { peerCertChain = decodeX509CertificateChain(decoder); } else { throw saslEntity.asnUnexpectedTag(); } return peerCertChain; } /** * Decode the next element from the given DER decoder as a trusted authorities element. * * @param decoder the DER decoder * @return the trusted authorities * @throws ASN1Exception if the next element from the given decoder is not a trusted authorities * element or if an error occurs while decoding the trusted authorities element */ public static List decodeTrustedAuthorities(final DERDecoder decoder) throws ASN1Exception { List trustedAuthorities = new ArrayList(); TrustedAuthority trustedAuthority = null; decoder.startSequence(); while (decoder.hasNextElement()) { out: { for (int trustedAuthorityType = 0; trustedAuthorityType <= 4; trustedAuthorityType++) { switch (trustedAuthorityType) { case AUTHORITY_NAME: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, trustedAuthorityType, true)) { byte[] encodedName = decoder.drainElementValue(); trustedAuthority = new NameTrustedAuthority((new X500Principal(encodedName)).getName(X500Principal.CANONICAL)); break out; } break; case AUTHORITY_CERTIFICATE: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, trustedAuthorityType, true)) { decoder.decodeImplicit(trustedAuthorityType); byte[] cert = decoder.drainElement(); cert[0] = SEQUENCE_TYPE; // Replace the trusted authority type tag with a DER SEQUENCE tag, as required by CertificateFactory#generateCertificate try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); trustedAuthority = new CertificateTrustedAuthority((X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(cert))); } catch (CertificateException e) { throw new ASN1Exception(e); } break out; } break; case ISSUER_NAME_HASH: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, trustedAuthorityType, false)) { decoder.decodeImplicit(trustedAuthorityType); trustedAuthority = new IssuerNameHashTrustedAuthority(decoder.decodeOctetString()); break out; } break; case ISSUER_KEY_HASH: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, trustedAuthorityType, false)) { decoder.decodeImplicit(trustedAuthorityType); trustedAuthority = new IssuerKeyHashTrustedAuthority(decoder.decodeOctetString()); break out; } break; case PKCS_15_KEY_HASH: if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, trustedAuthorityType, false)) { decoder.decodeImplicit(trustedAuthorityType); trustedAuthority = new PKCS15KeyHashTrustedAuthority(decoder.decodeOctetString()); break out; } break; default: throw saslEntity.asnInvalidGeneralNameType(); } } } trustedAuthorities.add(trustedAuthority); } decoder.endSequence(); return trustedAuthorities; } public static boolean matchGeneralNames(List generalNames, List actualGeneralNames) { if ((generalNames == null) || (actualGeneralNames == null)) { return false; } for (GeneralName generalName : generalNames) { for (GeneralName actualGeneralName : actualGeneralNames) { if (matchGeneralName(generalName, actualGeneralName)) { return true; } } } return false; } public static boolean matchGeneralName(GeneralName generalName, GeneralName actualGeneralName) { if ((generalName instanceof DNSName) && (actualGeneralName instanceof DirectoryName)) { // Check if the DNSName matches the DirectoryName's (most specific) Common Name field. // Although specifying a DNS name using the Common Name field has been deprecated, it is // still used in practice (e.g., see http://tools.ietf.org/html/rfc2818). String[] cnValues = X500PrincipalUtil.getAttributeValues(new X500Principal(((DirectoryName) actualGeneralName).getName()), X500.OID_AT_COMMON_NAME); String dnsName = ((DNSName) generalName).getName(); return dnsName.equalsIgnoreCase(cnValues[0]); } else { return generalName.equals(actualGeneralName); } } public static boolean matchGeneralNames(List generalNames, X509Certificate cert) { X500Principal certSubjectName = cert.getSubjectX500Principal(); try { if (matchGeneralNames(generalNames, convertToGeneralNames(cert.getSubjectAlternativeNames()))) { return true; } } catch (CertificateParsingException e) { // Ignore unless the subject name is empty if (certSubjectName == null) { throw saslEntity.unableToDetermineSubjectName(e); } } List certNames; if (certSubjectName != null) { certNames = new ArrayList(1); certNames.add(new DirectoryName(certSubjectName.getName(X500Principal.CANONICAL))); if (matchGeneralNames(generalNames, certNames)) { return true; } } return false; } public static String getDistinguishedNameFromGeneralNames(List generalNames) { for (GeneralName generalName : generalNames) { if (generalName instanceof DirectoryName) { return ((DirectoryName) generalName).getName(); } } return null; } private static GeneralName convertToGeneralName(List generalName) throws ASN1Exception { int type = ((Integer) generalName.get(0)).intValue(); Object name = generalName.get(1); switch (type) { case OTHER_NAME: return new OtherName((byte[]) name); case RFC_822_NAME: return new RFC822Name((String) name); case DNS_NAME: return new DNSName((String) name); case X400_ADDRESS: return new X400Address((byte[]) name); case DIRECTORY_NAME: return new DirectoryName((String) name); case EDI_PARTY_NAME: return new EDIPartyName((byte[]) name); case URI_NAME: return new URIName((String) name); case IP_ADDRESS: return new IPAddress((String) name); case REGISTERED_ID: return new RegisteredID((String) name); default: throw saslEntity.asnInvalidGeneralNameType(); } } private static List convertToGeneralNames(Collection> generalNames) throws ASN1Exception { if (generalNames == null) { return null; } List convertedGeneralNames = new ArrayList(); for (List generalName : generalNames) { convertedGeneralNames.add(convertToGeneralName(generalName)); } return convertedGeneralNames; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy