SharePoint 2010: Programatically Retrieve Credentials from the Secure Store Service

By | November 19, 2010

SharePoint 2010′s Secure Store Service provides a way to map credentials and delegate access to remote resources. You may already be familiar with the MOSS 2007 Single Sign-on Shared Service, which was the former equivalent. The Secure Store Service integrates seemlessly with Business Connectivity Services (BCS), but it also features an API that can be taken advantage of within custom development projects. This makes the service an attractive option for storing sensitive configuration data such as connection strings, Web service credentials, etc.

The Secure Store Service allows us to create Target Applications which house sets of credentials. The two main types are Individual and Group applications, Individual meaning credentials are mapped to individual users, and Group meaning all users share the same set of credentials.

While the raw API isn’t very intuitive, its design was likely intentional (additional security by obfuscation). With a little marshalling help from our interop library friends, we are able to retrieve credentials (provided the appropriate permissions to the target application).

To begin, we need to reference a couple of assemblies.

Microsoft.BusinessData.dll

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.BusinessData.dll

Microsoft.Office.SecureStoreService.dll

C:\Windows\assembly\GAC_MSIL\Microsoft.Office.SecureStoreService\14.0.0.0__71e9bce111e9429c\Microsoft.Office.SecureStoreService.dll

And now for the reason you came to this post … the code

using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.BusinessData.Infrastructure.SecureStore;
using Microsoft.Office.SecureStoreService.Server;
using Microsoft.SharePoint;
 
namespace Trentacular.SharePoint.Util
{
    public static class SecureStoreUtils
    {
        public static Dictionary<string, string> GetCredentials(string applicationID)
        {
            var serviceContext = SPServiceContext.Current;
            var secureStoreProvider = new SecureStoreProvider { Context = serviceContext };
            var credentialMap = new Dictionary<string, string>();
 
            using (var credentials = secureStoreProvider.GetCredentials(applicationID))
            {
                var fields = secureStoreProvider.GetTargetApplicationFields(applicationID);
                for (var i = 0; i < fields.Count; i++)
                {
                    var field = fields[i];
                    var credential = credentials[i];
                    var decryptedCredential = ToClrString(credential.Credential);
 
                    credentialMap.Add(field.Name, decryptedCredential);
                }
            }
 
            return credentialMap;
        }
 
        public static string ToClrString(this SecureString secureString)
        {
            var ptr = Marshal.SecureStringToBSTR(secureString);
 
            try
            {
                return Marshal.PtrToStringBSTR(ptr);
            }
            finally
            {
                Marshal.FreeBSTR(ptr);
            }
        }
    }
}

14 thoughts on “SharePoint 2010: Programatically Retrieve Credentials from the Secure Store Service

  1. Derek Martin

    This is great Trent – but how can I set/change a secure store password programatically? Also, how do I know that the secure store service only lets those people authorized to get the password can actually do so?

  2. Trent Post author

    You are jumping the gun on me. I’ll demonstrate how to change credentials in part 2 of this post.

  3. Trent Post author

    Regarding authorization to retrieve credentials, this is different based on the type of target application – Individual or Group. For Individual target application, the Secure Store Service maintains a mapping between users or claims and the set of credentials they can access. For Group target applications, the service keeps a list of members (either directory users or groups) that can access the set of shared credentials.

    When programmatically working with the API, the current thread identity determines the user or claim set used to verify access to a set of credentials. In a typical SharePoint HTTP Context, the identity is impersonating the calling user. Using SPSecurity.RunWithElevatedPriveleges will change the current identity to the identity of the application pool service account. This could be useful for delegating trust to a SharePoint Web application for accessing items such as shared connection strings in the Secure Store Service.

  4. Kaushal

    This implementation on itself will not work. You will need to add the interface methods.

    Example:
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security;
    using Microsoft.BusinessData.Infrastructure.SecureStore;
    using Microsoft.Office.SecureStoreService.Server;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    namespace Microsoft.SDK.Sharepoint.Samples
    {
    class Program
    {
    static void Main(string[] args)
    {
    // Get the default Secure Store Service provider.
    ISecureStoreProvider provider = SecureStoreProviderFactory.Create();
    if (provider == null)
    {
    throw new InvalidOperationException(“Unable to get an ISecureStoreProvider”);
    }

    ISecureStoreServiceContext providerContext = provider as ISecureStoreServiceContext;
    providerContext.Context = SPServiceContext.GetContext(GetCentralAdminSite());

    // Create the variables to hold the credentials.
    string userName = null;
    string password = null;
    string pin = null;
    // Specify a valid target application ID for the Secure Store.
    string appId = “mySecureStoreTargetApplication”;

    try
    {
    // Because we are getting the credentials in the using block, all the credentials that we get
    // will be disposed after the using block. If you need to cache the credentials, do not
    // use the using block, and dispose the credentials when you are finished.
    //
    // In the following block, we are looking for the first user name, password, and pin
    // credentials in the collection.
    using (SecureStoreCredentialCollection creds = provider.GetCredentials(appId))
    {
    // Secure Store Service will not return null. It may throw a SecureStoreServiceException,
    // but this may not be true for other providers.
    Debug.Assert(creds != null);

    if (creds != null)
    {
    foreach (SecureStoreCredential cred in creds)
    {
    if (cred == null)
    {
    // Secure Store Service will not return null credentials, but this may not be true for other providers.
    continue;
    }

    switch (cred.CredentialType)
    {
    case SecureStoreCredentialType.UserName:
    if (userName == null)
    {
    userName = GetStringFromSecureString(cred.Credential);
    }
    break;

    case SecureStoreCredentialType.Password:
    if (password == null)
    {
    password = GetStringFromSecureString(cred.Credential);
    }
    break;

    case SecureStoreCredentialType.Pin:
    if (pin == null)
    {
    pin = GetStringFromSecureString(cred.Credential);
    }
    break;
    }
    }
    }
    }

    if (userName == null || password == null || pin == null)
    {
    throw new InvalidOperationException(“Unable to get the credentials”);
    }

    // Use the credentials.
    //
    // Note that it is not a secure programming practice to print credential information, but this code example
    // prints the credentials to the console for testing purposes.
    Console.WriteLine(“User Name: ” + userName);
    Console.WriteLine(“Password : ” + password);
    Console.WriteLine(“Pin : ” + pin);
    }
    catch (SecureStoreException e)
    {
    Console.WriteLine(e.Message);
    throw;
    }
    }

    private static string GetStringFromSecureString(SecureString secStr)
    {
    if (secStr == null)
    {
    return null;
    }

    IntPtr pPlainText = IntPtr.Zero;
    try
    {
    pPlainText = Marshal.SecureStringToBSTR(secStr);
    return Marshal.PtrToStringBSTR(pPlainText);
    }
    finally
    {
    if (pPlainText != IntPtr.Zero)
    {
    Marshal.FreeBSTR(pPlainText);
    }
    }
    }

    public static SPSite GetCentralAdminSite()
    {
    SPAdministrationWebApplication adminWebApp = SPAdministrationWebApplication.Local;
    if (adminWebApp == null)
    {
    throw new InvalidProgramException(“Unable to get the admin web app”);
    }

    SPSite adminSite = null;
    Uri adminSiteUri = adminWebApp.GetResponseUri(SPUrlZone.Default);
    if (adminSiteUri != null)
    {
    adminSite = adminWebApp.Sites[adminSiteUri.AbsoluteUri];
    }
    else
    {
    throw new InvalidProgramException(“Unable to get Central Admin Site.”);
    }

    return adminSite;
    }
    }
    }

  5. anil

    Hi I have a question . we want to implement secure store service in our organization. But according to our policies we have to change our password every 3 months .So if we implement the secure store service for a user group , Is their a way that when the user changes his password the password change will also be implemented in secure store service .

  6. Sekhar

    Hi we have a requirement like, i added out look inbox and calender webpart, it should display the inbox and calender of the currently logged-in user without asking the credentials again. but it displaying the log-in page. Could you please let me know how to implement this.

  7. naveen

    Hi friends, I have created target application Id on central admin for Individual type. Now I have to set credentials for more user, so pls explain me how to set credentials sin SSS

  8. naveen

    I had this problem before setting x64 instead of x86. then i set x64 as target platform. When i am log in with spadmin credentials its works fine but its not work while i am log in with ordinary user account

  9. Dave

    An issue with the above code (much of it is provided by Microsoft) has a memory leak. The SPSite object that is returned from the method GetCentralAdminSite() is never disposed.

  10. Trent Post author

    Dave – just to be clear, you are referring to Kaushal’s comment that was ripped off the Microsoft site. The example I’ve posted in the actual post should not have this issue nor is ripped off from Microsoft.

  11. John

    Thanks for the article. I’m very new to sharepoint webapp development and this article helped me out alot.

    But just a small thing to point out (if it’s not obvious).

    SPServiceContext will not be available for Sandboxed Solution. So, if your application have the property Sandboxed Solution set to true, this class file will not be available to you.

  12. Hong

    Trent, Thanks for posting this solution, it works like a charm. :-)

Leave a Reply

Your email address will not be published. Required fields are marked *