/// <summary>Read SASL message and negotiated cipher option from server.</summary> /// <param name="in">stream to read</param> /// <returns> /// SaslResponseWithNegotiatedCipherOption SASL message and /// negotiated cipher option /// </returns> /// <exception cref="System.IO.IOException">for any error</exception> public static SaslResponseWithNegotiatedCipherOption ReadSaslMessageAndNegotiatedCipherOption (InputStream @in) { DataTransferProtos.DataTransferEncryptorMessageProto proto = DataTransferProtos.DataTransferEncryptorMessageProto .ParseFrom(PBHelper.VintPrefixed(@in)); if (proto.GetStatus() == DataTransferProtos.DataTransferEncryptorMessageProto.DataTransferEncryptorStatus .ErrorUnknownKey) { throw new InvalidEncryptionKeyException(proto.GetMessage()); } else { if (proto.GetStatus() == DataTransferProtos.DataTransferEncryptorMessageProto.DataTransferEncryptorStatus .Error) { throw new IOException(proto.GetMessage()); } else { byte[] response = proto.GetPayload().ToByteArray(); IList <CipherOption> options = PBHelper.ConvertCipherOptionProtos(proto.GetCipherOptionList ()); CipherOption option = null; if (options != null && !options.IsEmpty()) { option = options[0]; } return(new SaslResponseWithNegotiatedCipherOption(response, option)); } } }
/// <summary>Decrypt the key and iv of the negotiated cipher option.</summary> /// <param name="option">negotiated cipher option</param> /// <param name="sasl">SASL participant representing client</param> /// <returns> /// CipherOption negotiated cipher option which contains the /// decrypted key and iv /// </returns> /// <exception cref="System.IO.IOException">for any error</exception> public static CipherOption Unwrap(CipherOption option, SaslParticipant sasl) { if (option != null) { byte[] inKey = option.GetInKey(); if (inKey != null) { inKey = sasl.Unwrap(inKey, 0, inKey.Length); } byte[] outKey = option.GetOutKey(); if (outKey != null) { outKey = sasl.Unwrap(outKey, 0, outKey.Length); } return(new CipherOption(option.GetCipherSuite(), inKey, option.GetInIv(), outKey, option.GetOutIv())); } return(null); }
/// <summary>Send SASL message and negotiated cipher option to client.</summary> /// <param name="out">stream to receive message</param> /// <param name="payload">to send</param> /// <param name="option">negotiated cipher option</param> /// <exception cref="System.IO.IOException">for any error</exception> public static void SendSaslMessageAndNegotiatedCipherOption(OutputStream @out, byte [] payload, CipherOption option) { DataTransferProtos.DataTransferEncryptorMessageProto.Builder builder = DataTransferProtos.DataTransferEncryptorMessageProto .NewBuilder(); builder.SetStatus(DataTransferProtos.DataTransferEncryptorMessageProto.DataTransferEncryptorStatus .Success); if (payload != null) { builder.SetPayload(ByteString.CopyFrom(payload)); } if (option != null) { builder.AddCipherOption(PBHelper.Convert(option)); } DataTransferProtos.DataTransferEncryptorMessageProto proto = ((DataTransferProtos.DataTransferEncryptorMessageProto )builder.Build()); proto.WriteDelimitedTo(@out); @out.Flush(); }
/// <summary> /// Create IOStreamPair of /// <see cref="Org.Apache.Hadoop.Crypto.CryptoInputStream"/> /// and /// <see cref="Org.Apache.Hadoop.Crypto.CryptoOutputStream"/> /// </summary> /// <param name="conf">the configuration</param> /// <param name="cipherOption">negotiated cipher option</param> /// <param name="out">underlying output stream</param> /// <param name="in">underlying input stream</param> /// <param name="isServer">is server side</param> /// <returns>IOStreamPair the stream pair</returns> /// <exception cref="System.IO.IOException">for any error</exception> public static IOStreamPair CreateStreamPair(Configuration conf, CipherOption cipherOption , OutputStream @out, InputStream @in, bool isServer) { if (Log.IsDebugEnabled()) { Log.Debug("Creating IOStreamPair of CryptoInputStream and " + "CryptoOutputStream." ); } CryptoCodec codec = CryptoCodec.GetInstance(conf, cipherOption.GetCipherSuite()); byte[] inKey = cipherOption.GetInKey(); byte[] inIv = cipherOption.GetInIv(); byte[] outKey = cipherOption.GetOutKey(); byte[] outIv = cipherOption.GetOutIv(); InputStream cIn = new CryptoInputStream(@in, codec, isServer ? inKey : outKey, isServer ? inIv : outIv); OutputStream cOut = new CryptoOutputStream(@out, codec, isServer ? outKey : inKey , isServer ? outIv : inIv); return(new IOStreamPair(cIn, cOut)); }
public SaslResponseWithNegotiatedCipherOption(byte[] payload, CipherOption cipherOption ) { this.payload = payload; this.cipherOption = cipherOption; }
/// <summary>This method actually executes the client-side SASL handshake.</summary> /// <param name="underlyingOut">connection output stream</param> /// <param name="underlyingIn">connection input stream</param> /// <param name="userName">SASL user name</param> /// <param name="saslProps">properties of SASL negotiation</param> /// <param name="callbackHandler">for responding to SASL callbacks</param> /// <returns>new pair of streams, wrapped after SASL negotiation</returns> /// <exception cref="System.IO.IOException">for any error</exception> private IOStreamPair DoSaslHandshake(OutputStream underlyingOut, InputStream underlyingIn , string userName, IDictionary <string, string> saslProps, CallbackHandler callbackHandler ) { DataOutputStream @out = new DataOutputStream(underlyingOut); DataInputStream @in = new DataInputStream(underlyingIn); SaslParticipant sasl = SaslParticipant.CreateClientSaslParticipant(userName, saslProps , callbackHandler); @out.WriteInt(SaslTransferMagicNumber); @out.Flush(); try { // Start of handshake - "initial response" in SASL terminology. DataTransferSaslUtil.SendSaslMessage(@out, new byte[0]); // step 1 byte[] remoteResponse = DataTransferSaslUtil.ReadSaslMessage(@in); byte[] localResponse = sasl.EvaluateChallengeOrResponse(remoteResponse); IList <CipherOption> cipherOptions = null; if (DataTransferSaslUtil.RequestedQopContainsPrivacy(saslProps)) { // Negotiate cipher suites if configured. Currently, the only supported // cipher suite is AES/CTR/NoPadding, but the protocol allows multiple // values for future expansion. string cipherSuites = conf.Get(DFSConfigKeys.DfsEncryptDataTransferCipherSuitesKey ); if (cipherSuites != null && !cipherSuites.IsEmpty()) { if (!cipherSuites.Equals(CipherSuite.AesCtrNopadding.GetName())) { throw new IOException(string.Format("Invalid cipher suite, %s=%s", DFSConfigKeys. DfsEncryptDataTransferCipherSuitesKey, cipherSuites)); } CipherOption option = new CipherOption(CipherSuite.AesCtrNopadding); cipherOptions = Lists.NewArrayListWithCapacity(1); cipherOptions.AddItem(option); } } DataTransferSaslUtil.SendSaslMessageAndNegotiationCipherOptions(@out, localResponse , cipherOptions); // step 2 (client-side only) SaslResponseWithNegotiatedCipherOption response = DataTransferSaslUtil.ReadSaslMessageAndNegotiatedCipherOption (@in); localResponse = sasl.EvaluateChallengeOrResponse(response.payload); System.Diagnostics.Debug.Assert(localResponse == null); // SASL handshake is complete DataTransferSaslUtil.CheckSaslComplete(sasl, saslProps); CipherOption cipherOption = null; if (sasl.IsNegotiatedQopPrivacy()) { // Unwrap the negotiated cipher option cipherOption = DataTransferSaslUtil.Unwrap(response.cipherOption, sasl); } // If negotiated cipher option is not null, we will use it to create // stream pair. return(cipherOption != null?DataTransferSaslUtil.CreateStreamPair(conf, cipherOption , underlyingOut, underlyingIn, false) : sasl.CreateStreamPair(@out, @in)); } catch (IOException ioe) { DataTransferSaslUtil.SendGenericSaslErrorMessage(@out, ioe.Message); throw; } }
/// <summary>This method actually executes the server-side SASL handshake.</summary> /// <param name="underlyingOut">connection output stream</param> /// <param name="underlyingIn">connection input stream</param> /// <param name="saslProps">properties of SASL negotiation</param> /// <param name="callbackHandler">for responding to SASL callbacks</param> /// <returns>new pair of streams, wrapped after SASL negotiation</returns> /// <exception cref="System.IO.IOException">for any error</exception> private IOStreamPair DoSaslHandshake(OutputStream underlyingOut, InputStream underlyingIn , IDictionary <string, string> saslProps, CallbackHandler callbackHandler) { DataInputStream @in = new DataInputStream(underlyingIn); DataOutputStream @out = new DataOutputStream(underlyingOut); SaslParticipant sasl = SaslParticipant.CreateServerSaslParticipant(saslProps, callbackHandler ); int magicNumber = @in.ReadInt(); if (magicNumber != SaslTransferMagicNumber) { throw new InvalidMagicNumberException(magicNumber, dnConf.GetEncryptDataTransfer( )); } try { // step 1 byte[] remoteResponse = DataTransferSaslUtil.ReadSaslMessage(@in); byte[] localResponse = sasl.EvaluateChallengeOrResponse(remoteResponse); DataTransferSaslUtil.SendSaslMessage(@out, localResponse); // step 2 (server-side only) IList <CipherOption> cipherOptions = Lists.NewArrayList(); remoteResponse = DataTransferSaslUtil.ReadSaslMessageAndNegotiationCipherOptions( @in, cipherOptions); localResponse = sasl.EvaluateChallengeOrResponse(remoteResponse); // SASL handshake is complete DataTransferSaslUtil.CheckSaslComplete(sasl, saslProps); CipherOption cipherOption = null; if (sasl.IsNegotiatedQopPrivacy()) { // Negotiate a cipher option cipherOption = DataTransferSaslUtil.NegotiateCipherOption(dnConf.GetConf(), cipherOptions ); if (cipherOption != null) { if (Log.IsDebugEnabled()) { Log.Debug("Server using cipher suite " + cipherOption.GetCipherSuite().GetName()); } } } // If negotiated cipher option is not null, wrap it before sending. DataTransferSaslUtil.SendSaslMessageAndNegotiatedCipherOption(@out, localResponse , DataTransferSaslUtil.Wrap(cipherOption, sasl)); // If negotiated cipher option is not null, we will use it to create // stream pair. return(cipherOption != null?DataTransferSaslUtil.CreateStreamPair(dnConf.GetConf (), cipherOption, underlyingOut, underlyingIn, true) : sasl.CreateStreamPair(@out , @in)); } catch (IOException ioe) { if (ioe is SaslException && ioe.InnerException != null && ioe.InnerException is InvalidEncryptionKeyException) { // This could just be because the client is long-lived and hasn't gotten // a new encryption key from the NN in a while. Upon receiving this // error, the client will get a new encryption key from the NN and retry // connecting to this DN. SendInvalidKeySaslErrorMessage(@out, ioe.InnerException.Message); } else { DataTransferSaslUtil.SendGenericSaslErrorMessage(@out, ioe.Message); } throw; } }