Java tutorial
/* * Copyright (C)2016 - SMBJ Contributors * * 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 com.hierynomus.spnego; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.protocol.commons.buffer.Endian; import org.bouncycastle.asn1.*; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import static com.hierynomus.spnego.ObjectIdentifiers.SPNEGO; /** * This class can encode and decode the SPNEGO negTokenInit Token. * <p/> * The entire token is an ASN.1 DER encoded sequence of bytes in little endian byte encoding. * <p/> * The following is the full ASN.1 specification of the token: * <p/> * <pre> * GSSAPI ::= [APPLICATION 0] IMPLICIT SEQUENCE { * mech MechType, * negTokenInit NegotiationToken * } * * NegotiationToken ::= CHOICE { * negTokenInit [0] NegTokenInit, * negTokenTarg [1] NegTokenTarg * } * * NegTokenInit ::= SEQUENCE { * mechTypes [0] MechTypeList OPTIONAL, * reqFlags [1] ContextFlags OPTIONAL, * mechToken [2] OCTET STRING OPTIONAL, * mechListMIC [3] OCTET STRING OPTIONAL * } * * MechTypeList ::= SEQUENCE of MechType * * ContextFlags ::= BIT_STRING { * delegFlag (0), * mutualFlag (1), * replayFlag (2), * sequenceFlag (3), * anonFlag (4), * confFlag (5), * integFlag (6) * } * * MechType ::= OBJECT IDENTIFIER * </pre> * <p/> * In the context of this class only the <em>NegTokenInit</em> is covered. * <p/> * <ul> * <li>When an InitToken is sent, it is prepended by the generic GSSAPI header.</li> * <li>The "mech" field of the GSSAPI header is always set to the SPNEGO OID (1.3.6.1.5.5.2)</li> * <li>The negTokenInit will have a lead byte of <code>0xa0</code> (the CHOICE tagged object).</li> * </ul> */ public class NegTokenInit extends SpnegoToken { private static final String ADS_IGNORE_PRINCIPAL = "not_defined_in_RFC4178@please_ignore"; private List<ASN1ObjectIdentifier> mechTypes = new ArrayList<>(); private byte[] mechToken; public NegTokenInit() { super(0x0, "NegTokenInit"); } public void write(Buffer<?> buffer) throws SpnegoException { try { ASN1EncodableVector negTokenInit = new ASN1EncodableVector(); addMechTypeList(negTokenInit); addMechToken(negTokenInit); writeGss(buffer, negTokenInit); } catch (IOException e) { throw new SpnegoException("Unable to write NegTokenInit", e); } } public NegTokenInit read(byte[] bytes) throws SpnegoException { return read(new Buffer.PlainBuffer(bytes, Endian.LE)); } private NegTokenInit read(Buffer<?> buffer) throws SpnegoException { try (ASN1InputStream is = new ASN1InputStream(buffer.asInputStream())) { ASN1Primitive applicationSpecific = is.readObject(); if (!(applicationSpecific instanceof BERApplicationSpecific || applicationSpecific instanceof DERApplicationSpecific)) { throw new SpnegoException( "Incorrect GSS-API ASN.1 token received, expected to find an [APPLICATION 0], not: " + applicationSpecific); } ASN1Sequence implicitSequence = (ASN1Sequence) ((ASN1ApplicationSpecific) applicationSpecific) .getObject(BERTags.SEQUENCE); ASN1Encodable spnegoOid = implicitSequence.getObjectAt(0); if (!(spnegoOid instanceof ASN1ObjectIdentifier)) { throw new SpnegoException("Expected to find the SPNEGO OID (" + SPNEGO + "), not: " + spnegoOid); } parseSpnegoToken(implicitSequence.getObjectAt(1)); } catch (IOException ioe) { throw new SpnegoException("Could not read NegTokenInit from buffer", ioe); } return this; } @Override protected void parseTagged(ASN1TaggedObject asn1TaggedObject) throws SpnegoException { if (asn1TaggedObject.getObject().toString().contains(ADS_IGNORE_PRINCIPAL)) { // Ignore return; } switch (asn1TaggedObject.getTagNo()) { case 0: readMechTypeList(asn1TaggedObject.getObject()); break; case 1: // Ignore reqFlags for now... break; case 2: readMechToken(asn1TaggedObject.getObject()); break; case 3: // Ignore mechListMIC for now... break; default: throw new SpnegoException("Unknown Object Tag " + asn1TaggedObject.getTagNo() + " encountered."); } } private void readMechToken(ASN1Primitive mechToken) throws SpnegoException { if (!(mechToken instanceof ASN1OctetString)) { throw new SpnegoException("Expected the MechToken (OCTET_STRING) contents, not: " + mechToken); } this.mechToken = ((ASN1OctetString) mechToken).getOctets(); } private void readMechTypeList(ASN1Primitive sequence) throws SpnegoException { if (!(sequence instanceof ASN1Sequence)) { throw new SpnegoException("Expected the MechTypeList (SEQUENCE) contents, not: " + sequence); } Enumeration mechTypeElems = ((ASN1Sequence) sequence).getObjects(); while (mechTypeElems.hasMoreElements()) { ASN1Encodable mechType = (ASN1Encodable) mechTypeElems.nextElement(); if (!(mechType instanceof ASN1ObjectIdentifier)) { throw new SpnegoException( "Expected a MechType (OBJECT IDENTIFIER) as contents of the MechTypeList, not: " + mechType); } mechTypes.add((ASN1ObjectIdentifier) mechType); } } private void addMechToken(ASN1EncodableVector negTokenInit) { if (mechToken != null && mechToken.length > 0) { ASN1Primitive token = new DERTaggedObject(true, 0x02, new DEROctetString(mechToken)); negTokenInit.add(token); } } private void addMechTypeList(ASN1EncodableVector negTokenInit) { if (mechTypes.size() > 0) { ASN1EncodableVector supportedMechVector = new ASN1EncodableVector(); for (ASN1ObjectIdentifier mechType : mechTypes) { supportedMechVector.add(mechType); } ASN1Primitive asn1Encodables1 = new DERTaggedObject(true, 0x0, new DERSequence(supportedMechVector)); negTokenInit.add(asn1Encodables1); } } public void addSupportedMech(ASN1ObjectIdentifier oid) { this.mechTypes.add(oid); } public void setMechToken(byte[] mechToken) { this.mechToken = mechToken; } public List<ASN1ObjectIdentifier> getSupportedMechTypes() { return mechTypes; } }