title: Powershell System.Net.WebClient Authentication
body: <p>Using below code in attempt to download file from protected HTTP source returns a 401 Unauthorized:</p>
  
  <pre><code>$pfile = "\\some\local\leaf\path"
  $user = "MyUserName"
  
  $pwdIn = read-host -assecurestring | convertfrom-securestring | out-file $pfile
  $pwdOut = get-content $pFile| convertto-securestring
  
  $url="http://path.to/file"
  $file = "\\localpath\to\file"
  $webclient = new-object System.Net.WebClient
  $webclient.Credentials = new-object System.Net.NetworkCredential($user, $pwdOut)
  $webclient.DownloadFile($url,$file)
  </code></pre>
  
  <p>So does skipping the step of writing the password file like so:</p>
  
  <pre><code>$pwd = read-host -assecurestring 
  ...
  $webclient.Credentials = new-object System.Net.NetworkCredential($user, $pwd)
  </code></pre>
  
  <p>However, skipping securestring creation works just fine:</p>
  
  <pre><code> $pwd = read-host
   ...
   $webclient.Credentials = new-object System.Net.NetworkCredential($user, $pwd)
  </code></pre>
  
  <p>Above code does not return any other errors. I verified existence and apparent structural validity of the target password file when testing original code version at the top. The entire code is within single script, so convertto and convertfrom have identical environment during execution. I tried this in both x86 and 64bit powershell. </p>
  
  <p>In fact, I can even convert the secure string back to the correct basic string with and use it with WebClient:</p>
  
  <pre><code>$pwdtmp = get-content $pFile| convertto-securestring
  $pwdOut = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwdtmp))
  $webclient.Credentials = new-object System.Net.NetworkCredential($user, $pwdOut)
  </code></pre>
  
  <p>Thus, the problem must be with using SecureString with System.Net.NetworkCredential class, however <a href="http://msdn.microsoft.com/en-us/library/system.net.networkcredential.aspx" rel="nofollow">http://msdn.microsoft.com/en-us/library/system.net.networkcredential.aspx</a>, as well as some only examples i found indicate SecureString should work as second argument.</p>
  
  <p>While writing this post, I have in fact found a solution:</p>
  
  <pre><code>$user = "user"
  $pwd = read-host -assecurestring
  $creds = New-Object System.Management.Automation.PSCredential `
       -ArgumentList $user, $pwd  
  $webclient.Credentials = new-object System.Net.NetworkCredential($user, $Creds.GetNetworkCredential().Password)
  $webclient.DownloadFile($url,$file)
  </code></pre>
  
  <p>But not an explanation as to why SecureString type doesn't work, while PSCredential.Password does. Executing $creds|gm clearly indicates that $creds.Password is also in fact a SecureString:</p>
  
  <blockquote>
    <p>TypeName: System.Management.Automation.PSCredential</p>
    
    <p>Name                 MemberType Definition                            </p>
    
    <p>----                 ---------- ----------                                          Equals               Method     bool Equals(System.Object obj)<br>
    GetHashCode          Method     int GetHashCode()<br>
    GetNetworkCredential Method     System.Net.NetworkCredential
    GetNetworkCredential() GetType              Method     type GetType() 
    ToString             Method     string ToString()<br>
    Password             Property   System.Security.SecureString Password
    {get;}</p>
  </blockquote>
  
  <p>What gives?</p>