Download file with SmartCard Authentication

This topic contains 4 replies, has 2 voices, and was last updated by Profile photo of derek ebeling derek ebeling 1 year ago.

  • Author
  • #31078
    Profile photo of derek ebeling
    derek ebeling

    I need to download a PDF file from a SharePoint Server that requires SmartCard Authentication. This powershell file will need to be run multiple times a day and by different users. The only method of authentication is via SmartCard. How can I do this?

    I have tried the following method, where I first tried to retrieve credentials from:

    $object = new-object Microsoft.VisualBasic.Devices.Network
    Write-Host "Please Select SmartCard from dropdown and enter PIN.";
    $credential = $host.ui.PromptForCredential("Need credentials", "Please enter your user name and password.", "", "NetBiosUserName")

    then did the following:

    1) $web.Credentials = $credential
    $web.DownloadFile($url, $path)

    2) $object.DownloadFile($url, $path, $credential.username, $credential.password , $true, 500, $true, 'DoNothing')

    Neither method worked.

    If I create an instance of internet explorer I can open the PDF file, but I cannot get it to download automatically. Ideally, besides opening the powershell file and clicking authenticate on the SmartCard popup, everything should be invisible to the user. If not invisible, then everything would close down.

  • #31112
    Profile photo of Rohn Edwards
    Rohn Edwards

    Smart card authentication for a website can normally be treated like a website that requires a certificate. Windows will handle any operations that require the private key, and it will prompt you for your PIN when needed. If you can use Invoke-WebRequest, it has a -Certificate parameter and an -OutFile parameter for saving a file. Try this (it depends on $Url and $Path already being defined):

    Add-Type -AssemblyName System.Security
    # You can do more filtering here if there are other cert requirements...
    $ValidCerts = [System.Security.Cryptography.X509Certificates.X509Certificate2[]](dir Cert:\CurrentUser\My | where { $_.NotAfter -gt (Get-Date) })
    # You could check $ValidCerts, and not do this prompt if it only contains 1...
    $Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2UI]::SelectFromCollection(
        'Choose a certificate',
        'Choose a certificate',
    ) | select -First 1
    $IwrParams = @{
        Uri = $Url       # Uri to file to download
        OutFile = $Path  # Path to where file should be downloaded (include filename)
        Certificate = $Cert
    Invoke-WebRequest @IwrParams

    There are some scenarios where that still won't work, though, so let me know what happens when you try it...

  • #31152
    Profile photo of derek ebeling
    derek ebeling


    Thanks for the reply. Amateur hour here, sorry, I should have mentioned I am stuck with powershell v2. I was under the impression from earlier research that Invoke-webrequest is for version 3 and up; please correct me if I am wrong as I could not get it to work earlier. I have not had a chance to try your code. Any ideas for powershell v2?

  • #31157
    Profile photo of Rohn Edwards
    Rohn Edwards


    You're right, Invoke-WebRequest isn't available in v2. I think it just uses the .NET WebRequest and WebResponse objects, though, which should be available. Try this (don't forget to fill in the first three variables):

    $Url = 'URL HERE'
    $OutFilePath = 'OUTPUT FILE NAME HERE'
    $Cert = 'USER CERT HERE'
    if (Test-Path $OutFilePath) {
        throw "'$OutFilePath' already exists!"
    # Create webrequest that contains the selected certificate, and try to get a response
    $Request = [System.Net.WebRequest]::Create($Url)
    try {
        $Response = $Request.GetResponse()
    catch {
        # You could present a nicer message here
        Write-Error $_
    if ($Response) {
        # You'll probably want to check out the $Response object before doing anything with
        # it (probably at least check $Response.StatusCode)
        # There's probably a shorter/cleaner/better way to do this, but this will create a buffer and a filestream,
        # then transfer the binary data from the $Response's stream to the filestream using the buffer...
        $Buffer = New-Object byte[] 1024  # You can adjust the buffer size
        $OutFileStream = [System.IO.File]::Create($OutFilePath)   # This will overwrite an existing file!
        $ResponseStream = $Response.GetResponseStream()
        while (($BytesRead = $ResponseStream.Read($Buffer, 0, $Buffer.Length))) {
            $OutFileStream.Write($Buffer, 0, $BytesRead)
        # Cleanup
  • #31305
    Profile photo of derek ebeling
    derek ebeling


    You are a PowerShell Genius! Combining your two threads worked perfect.

You must be logged in to reply to this topic.