private async Task <string> GetRsaPublicKeyAsync(string switchRequestName, ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { if (!string.IsNullOrEmpty(cs.ServerRsaPublicKeyFile)) { try { return(File.ReadAllText(cs.ServerRsaPublicKeyFile)); } catch (IOException ex) { throw new MySqlException("Couldn't load server's RSA public key from '{0}'".FormatInvariant(cs.ServerRsaPublicKeyFile), ex); } } if (cs.AllowPublicKeyRetrieval) { // request the RSA public key var payloadContent = switchRequestName == "caching_sha2_password" ? (byte)0x02 : (byte)0x01; await SendReplyAsync(new PayloadData(new ArraySegment <byte>(new byte[] { payloadContent }, 0, 1)), ioBehavior, cancellationToken).ConfigureAwait(false); var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload); return(Encoding.ASCII.GetString(publicKeyPayload.Data)); } throw new MySqlException("Authentication method '{0}' failed. Either use a secure connection, specify the server's RSA public key with ServerRSAPublicKeyFile, or set AllowPublicKeyRetrieval=True.".FormatInvariant(switchRequestName)); }
private async Task SwitchAuthenticationAsync(ConnectionSettings cs, PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken) { // if the server didn't support the hashed password; rehash with the new challenge var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); switch (switchRequest.Name) { case "mysql_native_password": AuthPluginData = switchRequest.Data; var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password); payload = new PayloadData(new ArraySegment <byte>(hashedPassword)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); break; case "mysql_clear_password": if (!m_isSecureConnection) { throw new MySqlException("Authentication method '{0}' requires a secure connection.".FormatInvariant(switchRequest.Name)); } payload = new PayloadData(new ArraySegment <byte>(Encoding.UTF8.GetBytes(cs.Password))); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); break; case "sha256_password": // add NUL terminator to password var passwordBytes = Encoding.UTF8.GetBytes(cs.Password); Array.Resize(ref passwordBytes, passwordBytes.Length + 1); if (!m_isSecureConnection && passwordBytes.Length > 1) { #if NET45 throw new MySqlException("Authentication method '{0}' requires a secure connection (prior to .NET 4.6).".FormatInvariant(switchRequest.Name)); #else string publicKey; if (!string.IsNullOrEmpty(cs.ServerRsaPublicKeyFile)) { try { publicKey = File.ReadAllText(cs.ServerRsaPublicKeyFile); } catch (IOException ex) { throw new MySqlException("Couldn't load server's RSA public key from '{0}'".FormatInvariant(cs.ServerRsaPublicKeyFile), ex); } } else if (cs.AllowPublicKeyRetrieval) { // request the RSA public key await SendReplyAsync(new PayloadData(new ArraySegment <byte>(new byte[] { 0x01 }, 0, 1)), ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload); publicKey = Encoding.ASCII.GetString(publicKeyPayload.Data); } else { throw new MySqlException("Authentication method '{0}' failed. Either use a secure connection, specify the server's RSA public key with ServerRSAPublicKeyFile, or set AllowPublicKeyRetrieval=True.".FormatInvariant(switchRequest.Name)); } // load the RSA public key RSA rsa; try { rsa = Utility.DecodeX509PublicKey(publicKey); } catch (Exception ex) { throw new MySqlException("Couldn't load server's RSA public key; try using a secure connection instead.", ex); } using (rsa) { // XOR the password bytes with the challenge AuthPluginData = Utility.TrimZeroByte(switchRequest.Data); for (int i = 0; i < passwordBytes.Length; i++) { passwordBytes[i] ^= AuthPluginData[i % AuthPluginData.Length]; } // encrypt with RSA public key var encryptedPassword = rsa.Encrypt(passwordBytes, RSAEncryptionPadding.OaepSHA1); payload = new PayloadData(new ArraySegment <byte>(encryptedPassword)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); } #endif } else { // send plaintext password payload = new PayloadData(new ArraySegment <byte>(passwordBytes)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); } break; default: throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(switchRequest.Name)); } }