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

org.wildfly.security.sasl.entity.EntitySaslClient 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).

There is a newer version: 34.0.0.Final
Show 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.CONTEXT_SPECIFIC_MASK;
import static org.wildfly.security.mechanism._private.ElytronMessages.saslEntity;
import static org.wildfly.security.sasl.entity.Entity.keyType;

import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.SaslException;

import org.wildfly.common.Assert;
import org.wildfly.security.asn1.ASN1Exception;
import org.wildfly.security.asn1.DERDecoder;
import org.wildfly.security.asn1.DEREncoder;
import org.wildfly.security.auth.callback.CredentialCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.callback.TrustedAuthoritiesCallback;
import org.wildfly.security.credential.X509CertificateChainPrivateCredential;
import org.wildfly.security.evidence.X509PeerCertificateChainEvidence;
import org.wildfly.security.x500.GeneralName;
import org.wildfly.security.x500.GeneralName.DNSName;
import org.wildfly.security.x500.GeneralName.DirectoryName;
import org.wildfly.security.sasl.util.AbstractSaslClient;
import org.wildfly.security.x500.TrustedAuthority;

/**
 * SaslClient for the ISO/IEC 9798-3 authentication mechanism as defined by
 * RFC 3163.
 *
 * @author Farah Juma
 */
final class EntitySaslClient extends AbstractSaslClient {

    private static final int ST_CHALLENGE_RESPONSE = 1;
    private static final int ST_RESPONSE_SENT = 2;

    private final SecureRandom secureRandom;
    private final Signature signature;
    private final boolean mutual;
    private final String serverName;
    private byte[] randomA;
    private byte[] randomB;
    private X509Certificate[] clientCertChain;

    EntitySaslClient(final String mechanismName, final boolean mutual, final Signature signature, final SecureRandom secureRandom, final String protocol,
            final String serverName, final CallbackHandler callbackHandler, final String authorizationId) {
        super(mechanismName, protocol, serverName, callbackHandler, authorizationId, false, saslEntity);
        this.signature = signature;
        this.secureRandom = secureRandom;
        this.mutual = mutual;
        this.serverName = serverName;
    }

    @Override
    public void init() {
        setNegotiationState(ST_CHALLENGE_RESPONSE);
    }

    @Override
    protected byte[] evaluateMessage(final int state, final byte[] challenge) throws SaslException {
        switch (state) {
            case ST_CHALLENGE_RESPONSE: {
                final DERDecoder decoder = new DERDecoder(challenge);
                List trustedAuthorities = null;
                List entityB = null;
                try {
                    // == Parse message ==
                    decoder.startSequence();

                    // randomB
                    randomB = decoder.decodeOctetString();

                    // entityB
                    if ((serverName != null) && (! serverName.isEmpty())) {
                        entityB = new ArrayList(1);
                        entityB.add(new DNSName(serverName));
                    }
                    if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, 0, true)) {
                        decoder.decodeImplicit(0);
                        List decodedEntityB = EntityUtil.decodeGeneralNames(decoder);
                        if ((entityB != null) && (! EntityUtil.matchGeneralNames(decodedEntityB, entityB))) {
                            throw saslEntity.mechServerIdentifierMismatch().toSaslException();
                        }
                    }

                    // certPref
                    if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, 1, true)) {
                        decoder.decodeImplicit(1);
                        trustedAuthorities = EntityUtil.decodeTrustedAuthorities(decoder);
                    }
                    decoder.endSequence();
                } catch (ASN1Exception e) {
                    throw saslEntity.mechInvalidServerMessageWithCause(e).toSaslException();
                }

                // == Send response ==

                // Construct TokenAB, where:
                // TokenAB ::= SEQUENCE {
                //      randomA         RandomNumber,
                //      entityB         [0] GeneralNames OPTIONAL,
                //      certA           [1] CertData,
                //      authId          [2] GeneralNames OPTIONAL,
                //      signature       SIGNATURE { TBSDataAB }
                // }
                // TBSDataAB ::= SEQUENCE {
                //      randomA         RandomNumber,
                //      randomB         RandomNumber,
                //      entityB         [0] GeneralNames OPTIONAL,
                //      authId          [1] GeneralNames OPTIONAL
                // }
                // CertData ::= CHOICE {
                //      certificateSet  SET SIZE (1..MAX) OF Certificate
                //      certURL         IA5String (Note: No support for certificate URL)
                // }
                // SIGNATURE { ToBeSigned } ::= SEQUENCE {
                //      algorithm       AlgorithmIdentifier,
                //      signature       BIT STRING
                // }
                final DEREncoder encoder = new DEREncoder();
                try {
                    encoder.startSequence();

                    // randomA
                    randomA = EntityUtil.encodeRandomNumber(encoder, secureRandom);

                    // entityB
                    if (entityB != null) {
                        encoder.encodeImplicit(0);
                        EntityUtil.encodeGeneralNames(encoder, entityB);
                    }

                    // certA (try obtaining a certificate chain)
                    encoder.startExplicit(1);
                    TrustedAuthoritiesCallback trustedAuthoritiesCallback = new TrustedAuthoritiesCallback();
                    trustedAuthoritiesCallback.setTrustedAuthorities(trustedAuthorities); // Server's preferred certificates
                    CredentialCallback credentialCallback = new CredentialCallback(X509CertificateChainPrivateCredential.class, keyType(signature.getAlgorithm()));

                    PrivateKey privateKey;
                    try {
                        tryHandleCallbacks(trustedAuthoritiesCallback, credentialCallback);
                        final X509CertificateChainPrivateCredential clientCertChainPrivateCredential = credentialCallback.getCredential(X509CertificateChainPrivateCredential.class);
                        if (clientCertChainPrivateCredential != null) {
                            clientCertChain = clientCertChainPrivateCredential.getCertificateChain();
                            if ((clientCertChain != null) && (clientCertChain.length > 0)) {
                                EntityUtil.encodeX509CertificateChain(encoder, clientCertChain);
                            } else {
                                throw saslEntity.mechCallbackHandlerNotProvidedClientCertificate().toSaslException();
                            }
                            privateKey = clientCertChainPrivateCredential.getPrivateKey();
                        } else {
                            throw saslEntity.mechCallbackHandlerNotProvidedClientCertificate().toSaslException();
                        }
                    } catch (UnsupportedCallbackException e) {
                        throw saslEntity.mechCallbackHandlerNotProvidedClientCertificate().toSaslException();
                    }
                    encoder.endExplicit();

                    // authId
                    final String authorizationId = getAuthorizationId();
                    List authId = null;
                    if (authorizationId != null) {
                        encoder.encodeImplicit(2);
                        // TODO: Will authorizationId be a distinguished name or is a callback needed to
                        // determine the appropriate GeneralName type to use?
                        authId = new ArrayList(1);
                        authId.add(new DirectoryName(authorizationId));
                        EntityUtil.encodeGeneralNames(encoder, authId);
                    }

                    // Private key
                    if (privateKey == null) {
                        throw saslEntity.mechCallbackHandlerNotProvidedPrivateKey().toSaslException();
                    }

                    // TBSDataAB
                    final DEREncoder tbsEncoder = new DEREncoder();
                    tbsEncoder.startSequence();
                    tbsEncoder.encodeOctetString(randomA);
                    tbsEncoder.encodeOctetString(randomB);
                    if (entityB != null) {
                        tbsEncoder.encodeImplicit(0);
                        EntityUtil.encodeGeneralNames(tbsEncoder, entityB);
                    }
                    if (authId != null) {
                        tbsEncoder.encodeImplicit(1);
                        EntityUtil.encodeGeneralNames(tbsEncoder, authId);
                    }
                    tbsEncoder.endSequence();

                    // Signature
                    byte[] signatureBytes;
                    try {
                        signature.initSign(privateKey);
                        signature.update(tbsEncoder.getEncoded());
                        signatureBytes = signature.sign();
                    } catch (SignatureException | InvalidKeyException e) {
                        throw saslEntity.mechUnableToCreateSignature(e).toSaslException();
                    }

                    encoder.startSequence();
                    EntityUtil.encodeAlgorithmIdentifier(encoder, signature.getAlgorithm());
                    encoder.encodeBitString(signatureBytes);
                    encoder.endSequence();

                    encoder.endSequence();
                } catch (ASN1Exception e) {
                    throw saslEntity.mechUnableToCreateResponseToken(e).toSaslException();
                }
                setNegotiationState(ST_RESPONSE_SENT);
                return encoder.getEncoded();
            }
            case ST_RESPONSE_SENT: {
                if (mutual) {
                    final DERDecoder decoder = new DERDecoder(challenge);
                    List entityA = null;
                    try {
                        decoder.startSequence();
                        byte[] randomC = decoder.decodeOctetString();

                        if (decoder.isNextType(CONTEXT_SPECIFIC_MASK, 0, true)) {
                            decoder.decodeImplicit(0);
                            entityA = EntityUtil.decodeGeneralNames(decoder);
                            // Verify that entityA matches the client's distinguishing identifier
                            if (! EntityUtil.matchGeneralNames(entityA, getClientCertificate())) {
                                throw saslEntity.mechClientIdentifierMismatch().toSaslException();
                            }
                        }

                        // Get the server's certificate data and verify it
                        decoder.startExplicit(1);
                        final X509PeerCertificateChainEvidence evidence = new X509PeerCertificateChainEvidence(EntityUtil.decodeCertificateData(decoder));
                        decoder.endExplicit();
                        X509Certificate serverCert = evidence.getFirstCertificate();

                        EvidenceVerifyCallback evidenceVerifyCallback = new EvidenceVerifyCallback(evidence);
                        handleCallbacks(evidenceVerifyCallback);
                        if (! evidenceVerifyCallback.isVerified()) {
                            throw saslEntity.mechServerAuthenticityCannotBeVerified().toSaslException();
                        }

                        // Get the server's signature and verify it
                        decoder.startSequence();
                        decoder.skipElement();
                        byte[] serverSignature = decoder.decodeBitString();
                        decoder.endSequence();

                        final DEREncoder tbsEncoder = new DEREncoder();
                        tbsEncoder.startSequence();
                        tbsEncoder.encodeOctetString(randomB);
                        tbsEncoder.encodeOctetString(randomA);
                        tbsEncoder.encodeOctetString(randomC);
                        if (entityA != null) {
                            EntityUtil.encodeGeneralNames(tbsEncoder, entityA);
                        }
                        tbsEncoder.endSequence();

                        try {
                            signature.initVerify(serverCert);
                            signature.update(tbsEncoder.getEncoded());
                            if (! signature.verify(serverSignature)) {
                                setNegotiationState(FAILED_STATE);
                                throw saslEntity.mechServerAuthenticityCannotBeVerified().toSaslException();
                            }
                        } catch (SignatureException | InvalidKeyException e) {
                            throw saslEntity.mechUnableToVerifyServerSignature(e).toSaslException();
                        }
                        decoder.endSequence();
                    } catch (ASN1Exception e) {
                        throw saslEntity.mechInvalidServerMessageWithCause(e).toSaslException();
                    }
                } else {
                    if (challenge != null && challenge.length != 0) {
                        throw saslEntity.mechServerSentExtraMessage().toSaslException();
                    }
                }
                negotiationComplete();
                return null;
            }
        }
        throw Assert.impossibleSwitchCase(state);
    }

    @Override
    public void dispose() throws SaslException {
        clientCertChain = null;
    }

    private X509Certificate getClientCertificate() throws SaslException {
        if (clientCertChain == null || clientCertChain.length == 0) throw saslEntity.mechCallbackHandlerNotProvidedServerCertificate().toSaslException();
        return clientCertChain[0];
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy