Self Signed Certificate For SharePoint Online

Introduction

In this blog, we will understand how to generate a self-signed certificate for SharePoint Online. This can be very useful while connecting to SharePoint Online based on Application permission which we used to call run with elevated privileges.

There are multiple ways to create a self-signed certificate for SharePoint online which can be used with the Azure AD application for authentication. We will go through three different ways,

  1. PnP PowerShell based
  2. COM object-based
  3. AzureAD based

Let us understand both the approach in detail.

PnP PowerShell

This approach is easy but requires an additional PowerShell module. It reduces the lines of code to just a single command execution. This approach also takes care of creating an Azure Active directory application so all the things are taken care of.

To Install PnP PowerShell if not already installed run the below command,

Install-Module -Name "PnP.PowerShell"

Skip the above step if PnP PowerShell is already installed.

Now to get the self-signed certificate created run the below command as Administration.

Change the parameters Tenant, CertificatePassword as per your requirement.

$CertificatePassword="password"
$Tenant="testinglala.onmicrosoft.com"
$CertificateOutDir = ".\Certificates"
if (-not ( Test-Path -Path $CertificateOutDir -PathType Container )) {
	md $CertificateOutDir
}
$app = Register-PnPAzureADApp -ApplicationName "ConnectingToSharePointFromAzureFunction" -Tenant $Tenant -OutPath $CertificateOutDir `
	-CertificatePassword (ConvertTo-SecureString -String $CertificatePassword -AsPlainText -Force) `
	-Scopes "SPO.Sites.FullControl.All" `
	-Store CurrentUser -DeviceLogin
Write-Host $app.'AzureAppId/ClientId'
Write-Host $app.'Certificate Thumbprint'

This script will generate two files with .pfx and .cer extension which are our self-signed certificate hence using PnP PowerShell is simple and easy.

Outcome

Self Signed Certificate for SharePoint Online

COM object-based

In this approach, we would not require to add any additional PowerShell module but it contains many commands which get executed. Along with that, it does not create an Azure AD Application for us, hence we need to create it manually and upload our certificate before using it.

Save the below file with generateSelfSignedCertificate.ps1 extension and run it with the command displayed below,

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Creates a Self Signed Certificate for use in server to server authentication
.DESCRIPTION
.EXAMPLE
PS C:\> .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key.
.EXAMPLE
PS C:\> .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Password (ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force)
This will create a new self signed certificate with the common name "CN=MyCert". The password as specified in the Password parameter will be used to protect the private key
.EXAMPLE
PS C:\> .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Force
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key. If there is already a certificate with the common name you specified, it will be removed first.
#>
Param(

   [Parameter(Mandatory=$true)]
   [string]$CommonName,
   [Parameter(Mandatory=$true)]
   [DateTime]$StartDate,
   [Parameter(Mandatory=$true)]
   [DateTime]$EndDate,
   [Parameter(Mandatory=$false, HelpMessage="Will overwrite existing certificates")]
   [Switch]$Force,
   [Parameter(Mandatory=$false)]
   [SecureString]$Password
)
# DO NOT MODIFY BELOW
function CreateSelfSignedCertificate(){
    #Remove and existing certificates with the same common name from personal and root stores
    #Need to be very wary of this as could break something
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
    if($certs -ne $null -and $certs.Length -gt 0)
    {
        if($Force)
        {
            foreach($c in $certs)
            {
                remove-item $c.PSPath
            }
        } else {
            Write-Host -ForegroundColor Red "One or more certificates with the same common name (CN=$CommonName) are already located in the local certificate store. Use -Force to remove them";
            return $false
        }
    }
    $name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
    $name.Encode("CN=$CommonName", 0)
    $key = new-object -com "X509Enrollment.CX509PrivateKey.1"
    $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
    $key.KeySpec = 1
    $key.Length = 2048 
    $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
    $key.MachineContext = 1
    $key.ExportPolicy = 1 # This is required to allow the private key to be exported
    $key.Create()
    $serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
    $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1") # Server Authentication
    $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
    $ekuoids.add($serverauthoid)
    $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
    $ekuext.InitializeEncode($ekuoids)
    $cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
    $cert.InitializeFromPrivateKey(2, $key, "")
    $cert.Subject = $name
    $cert.Issuer = $cert.Subject
    $cert.NotBefore = $StartDate
    $cert.NotAfter = $EndDate
    $cert.X509Extensions.Add($ekuext)
    $cert.Encode()
    $enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
    $enrollment.InitializeFromRequest($cert)
    $certdata = $enrollment.CreateRequest(0)
    $enrollment.InstallResponse(2, $certdata, 0, "")
    return $true
}
function ExportPFXFile()
{
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    if($Password -eq $null)
    {
        $Password = Read-Host -Prompt "Enter Password to protect private key" -AsSecureString
    }
    $cert = Get-ChildItem -Path Cert:\LocalMachine\my | where-object{$_.Subject -eq "CN=$CommonName"}
    
    Export-PfxCertificate -Cert $cert -Password $Password -FilePath "$($CommonName).pfx"
    Export-Certificate -Cert $cert -Type CERT -FilePath "$CommonName.cer"
}
function RemoveCertsFromStore()
{
    # Once the certificates have been been exported we can safely remove them from the store
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
    foreach($c in $certs)
    {
        remove-item $c.PSPath
    }
}
if(CreateSelfSignedCertificate)
{
    ExportPFXFile
    RemoveCertsFromStore
}

Command to execute the above PowerShell script is as mentioned below,

.\generateSelfSignedCertificate.ps1 -CommonName "testinglala" -StartDate 2021-02-04 -EndDate 2052-02-04

Note
The best practice here would be to use the tenant name as the CommonName.

Outcome

Self Signed Certificate for SharePoint Online

AzureAD based

In this approach as well we would require to add an additional module before executing the command and this would also not create an Azure AD application. Hence we would require to manually create an Azure AD application and upload the generated certificate.

To Install the Azure AD module run the below command,

Install-Module -Name AzureAD

Skip the command if the AzureAD module is already present.

Run the below script to generate a self-signed certificate please change the parameters as per your requirement and tenant details,

$dnsName = "testinglala.sharepoint.com"
$password = "password"
$folderPath = "C:\temp"
$fileName = "testinglala"
$yearsValid = 10 

$certStoreLocation = "cert:\LocalMachine\My"
$expirationDate = (Get-Date).AddYears($yearsValid)
    
$certificate = New-SelfSignedCertificate -DnsName $dnsName -CertStoreLocation $certStoreLocation -NotAfter $expirationDate -KeyExportPolicy Exportable -KeySpec Signature
    
$certificatePath = $certStoreLocation + '\' + $certificate.Thumbprint
$filePath = $folderPath + '\' + $fileName
$securePassword = ConvertTo-SecureString -String $password -Force -AsPlainText
Export-Certificate -Cert $certificatePath -FilePath ($filePath + '.cer')
Export-PfxCertificate -Cert $certificatePath -FilePath ($filePath + '.pfx') -Password $securePassword

Outcome

Self Signed Certificate for SharePoint Online

Conclusion

In this blog, we came across three different ways of creating a self-signed certificate but the easiest approach is to use PnP PowerShell which would help us to create all the required things quickly and easily but it would require PnP PowerShell module installed in your environment.