OAuth 2.0 with Gmail over IMAP for installed applications

March 13th, 2017

OAuth is an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

This article describes using OAuth 2.0 to access Gmail IMAP and SMTP servers using .NET IMAP component in installed applications scenario. You can also use OAuth 2.0 for web applications.

Google.Apis

Use Nuget to download “Google.Apis.Auth” package.

Import namespaces:

// c#

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;

using Limilabs.Client.Authentication.Google;

using Limilabs.Client.IMAP;
' VB.NET 

Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Auth.OAuth2.Flows
Imports Google.Apis.Auth.OAuth2.Requests
Imports Google.Apis.Auth.OAuth2.Responses

Imports Limilabs.Client.Authentication.Google

Imports Limilabs.Client.IMAP

Register Application

Before you can use OAuth 2.0, you must register your application using the Google Developers Console.

At least product name must be specified:

Now create credentials:

After you’ve registered, copy the “Client ID” and “Client secret” values, which you’ll need later:

Now we can define clientID, clientSecret and scope variables, as well as Google OAuth 2.0 server addresses. Scope basically specifies what services we want to have access to. In our case it is user’s email address and IMAP/SMTP access:

// c#

string clientID = "XXX.apps.googleusercontent.com";
string clientSecret = "IxBs0g5sdaSDUz4Ea7Ix-Ua";

var clientSecrets = new ClientSecrets
{
    ClientId = clientID,
    ClientSecret = clientSecret
};

var credential = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
    ClientSecrets = clientSecrets,
    Scopes = new[] { GoogleScope.ImapAndSmtp.Name, GoogleScope.EmailScope.Name}
});
' VB.NET 

Dim clientID As String = "XXX.apps.googleusercontent.com"
Dim clientSecret As String = "IxBs0g5sdaSDUz4Ea7Ix-Ua"

Dim clientSecrets = New ClientSecrets() With { _
	Key .ClientId = clientID, _
	Key .ClientSecret = clientSecret _
}

Dim credential = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer() With { _
	Key .ClientSecrets = clientSecrets, _
	Key .Scopes = New () {GoogleScope.ImapAndSmtp.Name, GoogleScope.EmailScope.Name} _
})

Turn on Google+ API

Remember to turn on Google+ API:
Turn on Google+ API

It is required to obtain email address of the user.

Obtain an OAuth 2.0 access token

Now we’ll create authorization url:


AuthorizationCodeRequestUrl url = credential.CreateAuthorizationCodeRequest("urn:ietf:wg:oauth:2.0:oob");

Process.Start(url.Build().ToString());
' VB.NET 

Dim url As AuthorizationCodeRequestUrl = credential.CreateAuthorizationCodeRequest("urn:ietf:wg:oauth:2.0:oob")

Process.Start(url.Build().ToString())

We are using Process.Start here, but you can also embed WebBrowser control in your application.

At this point user is redirected to Google to authorize the access:

After this step user is presented a code that needs to be pasted to your application:

Please note that this code also appears in the title of the browser:

  • It is possible to monitor processes on your machine and act automatically when it is there.
  • If you use embedded WebBrowser control in your application, you can monitor the HTML document title after any redirect.

Following is a code that reads this code and contacts Google to exchange it for a refresh-token and an access-token:

string authCode = Console.ReadLine();

TokenResponse token = await credential.ExchangeCodeForTokenAsync("", authCode, "urn:ietf:wg:oauth:2.0:oob", CancellationToken.None);

string accessToken = token.AccessToken;
' VB.NET 

Dim authCode As String = Console.ReadLine()

Dim token As TokenResponse = Await credential.ExchangeCodeForTokenAsync("", authCode, "urn:ietf:wg:oauth:2.0:oob", CancellationToken.None)

Dim accessToken As String = token.AccessToken

An access token is usually valid for a maximum of one hour, and allows you to access the user’s data. You also received a refresh token. A refresh token can be used to request a new access token once the previous expired.

Access IMAP/SMTP server

Finally we’ll ask Google for user’s email and use LoginOAUTH2 method to access Gmail’s IMAP server:

// c#

GoogleApi api = new GoogleApi(accessToken);
string user = api.GetEmailPlus();

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.LoginOAUTH2(user, accessToken);

    imap.SelectInbox();
    List<long> uids = imap.Search(Flag.Unseen);

    foreach (long uid in uids)
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        Console.WriteLine(email.Subject);
    }
    imap.Close();
}
' VB.NET 

Dim api As New GoogleApi(accessToken)
Dim user As String = api.GetEmailPlus()

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.LoginOAUTH2(user, accessToken)

	imap.SelectInbox()
	Dim uids As List(Of Long) = imap.Search(Flag.Unseen)

	For Each uid As Long In uids
		Dim eml = imap.GetMessageByUID(uid)
		Dim email As IMail = New MailBuilder().CreateFromEml(eml)
		Console.WriteLine(email.Subject)
	Next
	imap.Close()
End Using

Refreshing access token

An access token is usually short lived and valid for a maximum of one hour. The main reason behind this is security and prevention of replay attacks. This means that for long-lived applications you need to refresh the access token.

Your refresh token will be sent only once – don’t loose it!

We recommend storing entire TokenResponse object received from GoogleAuthorizationCodeFlow.ExchangeCodeForTokenAsync method call. This object contains both: refresh token and access token, along with its expiration time.

The process of refreshing access token is simple:

// c#

TokenResponse refreshed = await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None);

' VB.NET 

Dim refreshed As TokenResponse = Await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None)

OAuth 2.0 with Gmail over IMAP for web applications

March 13th, 2017

OAuth is an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

This article describes using OAuth 2.0 to access Gmail IMAP and SMTP servers using .NET IMAP component in web application scenario (ASP.NET/ASP.NET MVC). You can also use OAuth 2.0 for installed applications.

Google.Apis

Use Nuget to download “Google.Apis.Auth” package.

Import namespaces:

// c#

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;

using Limilabs.Client.Authentication.Google;

using Limilabs.Client.IMAP;
' VB.NET 

Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Auth.OAuth2.Flows
Imports Google.Apis.Auth.OAuth2.Requests
Imports Google.Apis.Auth.OAuth2.Responses

Imports Limilabs.Client.Authentication.Google

Imports Limilabs.Client.IMAP

Register Application

Before you can use OAuth 2.0, you must register your application using the Google Developers Console. After you’ve registered, go to the API Access tab and copy the “Client ID” and “Client secret” values and specify “Redirect URI“, which you’ll need later.

At least product name must be specified:

Now create credentials:

Specify redirect URI:

After you’ve registered, copy the “Client ID” and “Client secret” values, which you’ll need later:

Now we can define clientID, clientSecret, redirect url and scope variables, as well as Google OAuth 2.0 server addresses. Scope basically specifies what services we want to have access to. In our case it is user’s email address and IMAP/SMTP access:

// c#

string clientID = "XXX.apps.googleusercontent.com";
string clientSecret = "IxBs0g5sdaSDUz4Ea7Ix-Ua";
string redirectUri = "http://www.yourdomain.com/oauth2callback";

var clientSecrets = new ClientSecrets
{
    ClientId = clientID,
    ClientSecret = clientSecret
};

var credential = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
    ClientSecrets = clientSecrets,
    Scopes = new[] { GoogleScope.ImapAndSmtp.Name, GoogleScope.EmailScope.Name}
});
' VB.NET 

Dim clientID As String = "XXX.apps.googleusercontent.com"
Dim clientSecret As String = "IxBs0g5sdaSDUz4Ea7Ix-Ua"
Dim redirectUri As String = "http://www.yourdomain.com/oauth2callback"

Dim clientSecrets = New ClientSecrets() With { _
	Key .ClientId = clientID, _
	Key .ClientSecret = clientSecret _
}

Dim credential = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer() With { _
	Key .ClientSecrets = clientSecrets, _
	Key .Scopes = New () {GoogleScope.ImapAndSmtp.Name, GoogleScope.EmailScope.Name} _
})

Turn on Google+ API

Remember to turn on Google+ API:
Turn on Google+ API

It is required to obtain email address of the user.

Obtain an OAuth 2.0 access token

Now we’ll create authorization url:


AuthorizationCodeRequestUrl url = credential.CreateAuthorizationCodeRequest(redirectUri);

' VB.NET 

Dim url As AuthorizationCodeRequestUrl = credential.CreateAuthorizationCodeRequest(redirectUri)

Now we need to redirect the client:

// c#

return new RedirectResult(url.Build().ToString());

' VB.NET 

Return New RedirectResult(url.Build().ToString())

At this point user is redirected to Google to authorize the access:

After this step user is redirected back to your website (http://www.yourdomain.com/oauth2callback), with code request parameter:
http://www.yourdomain.com/oauth2callback?code=4/5Y7M4cARD9hrt0nuKnQa0YgtYMasdbwprRtIIjk4Fus#

// c#

public class OAauth2CallbackController : Controller
{
    public ActionResult Index(string code)
    {
        ...
    }
}
' VB.NET 

Public Class OAauth2CallbackController
    Inherits Controller
    Public Function Index(code As String) As ActionResult
        ...
    End Function
End Class

Following is this callback code. Its purpose is to get a refresh-token and an access-token:

// c#

string authCode = code;

TokenResponse token = await credential.ExchangeCodeForTokenAsync("", authCode, redirectUri, CancellationToken.None);

string accessToken = token.AccessToken;
' VB.NET 

Dim authCode As String = code

Dim token As TokenResponse = Await credential.ExchangeCodeForTokenAsync("", authCode, redirectUri, CancellationToken.None)

Dim accessToken As String = token.AccessToken

An access token is usually valid for a maximum of one hour, and allows you to access the user’s data. You also received a refresh token. A refresh token can be used to request a new access token once the previous expired.

Access IMAP/SMTP server

Finally we’ll ask Google for user’s email and use LoginOAUTH2 method to access Gmail’s IMAP server:

// c#

GoogleApi api = new GoogleApi(accessToken);
string user = api.GetEmailPlus();

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.LoginOAUTH2(user, accessToken);

    imap.SelectInbox();
    List<long> uids = imap.Search(Flag.Unseen);

    foreach (long uid in uids)
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        Console.WriteLine(email.Subject);
    }
    imap.Close();
}
' VB.NET 

Dim api As New GoogleApi(accessToken)
Dim user As String = api.GetEmailPlus()

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.LoginOAUTH2(user, accessToken)

	imap.SelectInbox()
	Dim uids As List(Of Long) = imap.Search(Flag.Unseen)

	For Each uid As Long In uids
		Dim eml = imap.GetMessageByUID(uid)
		Dim email As IMail = New MailBuilder().CreateFromEml(eml)
		Console.WriteLine(email.Subject)
	Next
	imap.Close()
End Using

Refreshing access token

An access token is usually short lived and valid for a maximum of one hour. The main reason behind this is security and prevention of replay attacks. This means that for long-lived applications you need to refresh the access token.

Your refresh token will be sent only once – don’t loose it!

We recommend storing entire TokenResponse object received from GoogleAuthorizationCodeFlow.ExchangeCodeForTokenAsync method call. This object contains both: refresh token and access token, along with its expiration time.

The process of refreshing access token is simple:

// c#

TokenResponse refreshed = await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None);

' VB.NET 

Dim refreshed As TokenResponse = Await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None)

OAuth 2.0 with Gmail over IMAP for installed applications (DotNetOpenAuth)

March 13th, 2017

OAuth is an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

This article describes using OAuth 2.0 to access Gmail IMAP and SMTP servers using .NET IMAP component in installed applications scenario. You can also use OAuth 2.0 for web applications.

DotNetOpenAuth

First download the latest version of DotNetOpenAuth – it’s free, open source library that implements OAuth 2.0: http://www.dotnetopenauth.net

Register Application

Before you can use OAuth 2.0, you must register your application using the Google Developers Console.

At least product name must be specified:

Now create credentials:

After you’ve registered, copy the “Client ID” and “Client secret” values, which you’ll need later:

Now we can define clientID, clientSecret and scope variables, as well as Google OAuth 2.0 server addresses. Scope basically specifies what services we want to have access to. In our case it is user’s email address and IMAP/SMTP access:

const string clientID = "12345.apps.googleusercontent.com";
const string clientSecret = "XXXYYY111";

AuthorizationServerDescription server = new AuthorizationServerDescription
    {
        AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
        TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
        ProtocolVersion = ProtocolVersion.V20,
    };
List<string> scope = new List<string> 
    { 
        GoogleScope.ImapAndSmtp.Name,
        GoogleScope.EmailScope.Name 
    };

Turn on Google+ API

Remember to turn on Google+ API:
Turn on Google+ API

It is required to obtain email address of the user.

Obtain an OAuth 2.0 access token

As we are using installed applications scenario we’ll use NativeApplicationClient class.

Because of a small issue in DotNetOpenAuth we can not use UserAgentClient directly. NativeApplicationClient inherits UserAgentClient and workarounds this issue. You can find the implementation of NativeApplicationClient on the bottom of this article.

NativeApplicationClient consumer = new NativeApplicationClient(server, clientID, clientSecret);
Uri userAuthorizationUri = consumer.RequestUserAuthorization(scope);

Process.Start(userAuthorizationUri.AbsoluteUri);

We are using Process.Start here, but you can also embed WebBrowser control in your application.

At this point user is redirected to Google to authorize the access:

After this step user is presented a code that needs to be pasted to your application:

Please note that this code also appears in the title of the browser:

  • It is possible to monitor processes on your machine and act automatically when it is there.
  • If you use embedded WebBrowser control in your application, you can monitor the HTML document title after any redirect.

Following is a code that reads this code and contacts Google to exchange it for a refresh-token and an access-token:

string authCode = Console.ReadLine();

consumer.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(clientSecret);

IAuthorizationState grantedAccess = consumer.ProcessUserAuthorization(authCode);

string accessToken = grantedAccess.AccessToken;

An access token is usually valid for a maximum of one hour, and allows you to access the user’s data. You also received a refresh token. A refresh token can be used to request a new access token once the previous expired.

Access IMAP/SMTP server

Finally we’ll ask Google for user’s email and use LoginOAUTH2 method to access Gmail’s IMAP server:

GoogleApi api = new GoogleApi(accessToken);
string user = api.GetEmailPlus();

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.LoginOAUTH2(user, accessToken);

    imap.SelectInbox();
    List<long> uids = imap.Search(Flag.Unseen);

    foreach (long uid in uids)
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        Console.WriteLine(email.Subject);
    }
    imap.Close();
}

NativeApplicationClient class

The OAuth 2.0 client for use by native applications. It’s main purpose is to build a generic URL containing the auth code, because DotNetOpenAuth library only allows parsing an URL as a method of retrieving the AuthorizationState.

/// <summary>
/// The OAuth2 client for use by native applications. 
/// This is a partial implementation which  should be used until 
/// the feature has been fully implemented in DotNetOpenAuth.
/// </summary>
public class NativeApplicationClient : UserAgentClient
{
    /// <summary>
    /// Represents a callback URL which points to a special out of band page 
    /// used for native OAuth2 authorization. This URL will cause the authorization 
    /// code to appear in the title of the window.
    /// </summary>
    /// <remarks>
    /// See http://code.google.com/apis/accounts/docs/OAuth2.html
    /// </remarks>
    public const string OutOfBandCallbackUrl = "urn:ietf:wg:oauth:2.0:oob";

    /// <summary>
    /// Initializes a new instance of the <see cref="UserAgentClient"/> class.
    /// </summary>
    /// <param name="authorizationServer">The token issuer.</param>
    /// <param name="clientIdentifier">The client identifier.</param>
    /// <param name="clientSecret">The client secret.</param>
    public NativeApplicationClient(
        AuthorizationServerDescription authorizationServer,
        string clientIdentifier,
        string clientSecret)
        : base(authorizationServer, clientIdentifier, clientSecret)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="UserAgentClient"/> class.
    /// </summary>
    /// <param name="authorizationServer">The token issuer.</param>
    public NativeApplicationClient(
        AuthorizationServerDescription authorizationServer)
        : this(authorizationServer, null, null)
    {
    }

    /// <summary>
    /// Creates the URL which should be used by the user to request the initial 
    /// authorization. Uses the default Out-of-band-URI as a callback.
    /// </summary>
    /// <param name="scope">Set of requested scopes</param>
    /// <returns>URI pointing to the authorization server</returns>
    public Uri RequestUserAuthorization(IEnumerable<string> scope)
    {
        var state = new AuthorizationState(scope);
        state.Callback = new Uri(OutOfBandCallbackUrl);
        return RequestUserAuthorization(state, false, null);
    }

    /// <summary>
    /// Uses the provided authorization code to create an authorization state.
    /// </summary>
    /// <param name="authCode">The authorization code for getting an access token.</param>
    /// <param name="authorizationState">The authorization.  Optional.</param>
    /// <returns>The granted authorization, or <c>null</c> if the authorization was null or rejected.</returns>
    public IAuthorizationState ProcessUserAuthorization(
        string authCode, 
        IAuthorizationState authorizationState)
    {
        if (authorizationState == null)
        {
            authorizationState = new AuthorizationState(null);
            authorizationState.Callback = new Uri(OutOfBandCallbackUrl);
        }

        // Build a generic URL containing the auth code.
        // This is done here as we cannot modify the DotNetOpenAuth library 
        // and the underlying method only allows parsing an URL as a method 
        // of retrieving the AuthorizationState.
        string url = "http://example.com/?code=" + authCode;
        return ProcessUserAuthorization(new Uri(url), authorizationState);
    }

    /// <summary>
    /// Uses the provided authorization code to create an authorization state.
    /// </summary>
    /// <param name="authCode">The authorization code for getting an access token.</param>
    /// <returns>The granted authorization, or <c>null</c> if the authorization was null or rejected.</returns>
    public IAuthorizationState ProcessUserAuthorization(string authCode)
    {
        return ProcessUserAuthorization(authCode, null);
    }
};

Refreshing access token

An access token is usually short lived and valid for a maximum of one hour. The main reason behind this is security and prevention of replay attacks. This means that for long-lived applications you need to refresh the access token.

Your refresh token will be sent only once – don’t loose it!

We recommend storing entire IAuthorizationState object received from NativeApplicationClient.ProcessUserAuthorization method call. This object contains both: refresh token and access token, along with its expiration time.

The process of refreshing access token is simple:

IAuthorizationState grantedAccess = ...
consumer.RefreshAuthorization(grantedAccess, TimeSpan.FromMinutes(20));

In the example above the access token will not be refreshed if its remaining lifetime exceeds 20 minutes.

Retrieving lost refresh token

When your application receives a refresh token, it is important to store that refresh token for future use. If your application loses the refresh token, it will have to re-prompt the user for consent before obtaining another refresh token.

You’ll need to add approval_prompt=force to your parameters:

 AuthorizationServerDescription authServer = new AuthorizationServerDescription
 {
     AuthorizationEndpoint = 
          new Uri("https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force"),
     ...
 };

Order process maintenance scheduled for March 7, 2017

March 2nd, 2017

As part of our ongoing commitment to invest in and provide a scalable ecommerce platform that meets the advancing needs of our client base, we will be conducting planned maintenance to our system next week:

CET: Tuesday, March 7, 2017 from 2 p.m. to 6 p.m.
CST: Tuesday, March 7, 2017 from 7 a.m. to 11 a.m.
PST: Tuesday, March 7, 2017 from 5 a.m. to 9 a.m.
AEDT: Wednesday, March 8, 2017 from midnight to 4 a.m.

During the planned maintenance, the system will continue to take orders. However customers will see temporary delays in fulfillment and order confirmation emails.

Once the maintenance is finished, we expect all functionality to resume; orders will be processed and order confirmation emails will be sent to customers.

How to log System.Net

January 3rd, 2017

In your App.config add the following code:

<?xml version="1.0"?>
<configuration>
  <system.diagnostics>

    <trace autoflush="true"/>

    <switches>
      <add name="System.Net" value="Verbose" />
    </switches>

    <sources>
      
      <source name="System.Net">
        <listeners>
          <add name="SystemNetLogFile"/>
        </listeners>
      </source>
      
    </sources>

    <sharedListeners>
      <add name="SystemNetLogFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="d:\system.net.log"/>
    </sharedListeners>

  </system.diagnostics>
</configuration>

That’s it!

The log looks like this:

System.Net Information: 0 : [8548] SecureChannel#238379
System.Net Information: 0 : [8548] Enumerating security
System.Net Information: 0 : [8548] Negotiate
System.Net Information: 0 : [8548] NegoExtender
System.Net Information: 0 : [8548] pku2u
System.Net Information: 0 : [8548] WDigest
System.Net Information: 0 : [8548] Kerberos
System.Net Information: 0 : [8548] NTLM
System.Net Information: 0 : [8548] Schannel
System.Net Information: 0 : [8548] Microsoft Unifie
System.Net Information: 0 : [8548] TSSSP
System.Net Information: 0 : [8548] CREDSSP
System.Net Information: 0 : [8548] SecureChannel#238379
System.Net Information: 0 : [8548] Current OS installat
System.Net Information: 0 : [8548] AcquireCredentialsHa
System.Net Error: 0 : [8548] AcquireCredentialsHandle()
System.Net Information: 0 : [8548] AcquireCredentialsHa
System.Net Error: 0 : [8548] AcquireCredentialsHandle()
System.Net Information: 0 : [8548] SecureChannel#214541
System.Net Information: 0 : [8548] SecureChannel#214541
System.Net Information: 0 : [8548] AcquireCredentialsHa
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] InitializeSecurityCo
System.Net Information: 0 : [8548] Remote certificate:
V3

[Subject]
CN=mail.postecert.it, O=Postecom S.p.A., L=Roma, S=Ro
Simple Name: mail.postecert.it
DNS Name: mail.postecert.it

[Issuer]
CN=GlobalSign Organization Validation CA - SHA256 - G
Simple Name: GlobalSign Organization Validation CA -
DNS Name: GlobalSign Organization Validation CA - SHA

[Serial Number]
0A9141CFE287B47FF4273C12

[Not Before]
2016-11-02 08:01:04

[Not After]
2017-11-03 08:01:04

[Thumbprint]
F12F74096659D9CCAA4ACD3432C896AF39CA8B9E

[Signature Algorithm]
sha256RSA(1.2.840.113549.1.1.11)

[Public Key]
Algorithm: RSA
Length: 2048
Key Blob: 30 82 01 0a 02 82 01 01 00 bf a1 62 b7 64 1
System.Net Information: 0 : [8548] SecureChannel#214541
System.Net Information: 0 : [8548] ProcessAuthenticatio