question

hitesh81 avatar image
0 Votes"
hitesh81 asked MikeEangelo-6600 answered

Unable to export App Service Certificate

Hello there!

I have followed the link and tried to export the certificate for Dev VM, but this is failing
link : Creating-a-local-PFX-copy-of-App-Service-Certificate.html

Error:

     New-Object:
     Line |
       43 |  … CertObject= New-Object System.Security.Cryptography.X509Certificates. …
          |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | Exception calling ".ctor" with "3" argument(s): "Array may not be empty or null. (Parameter 'rawData')"
     InvalidOperation:
     Line |
       48 |      [io.file]::WriteAllBytes(".\appservicecertificate.pfx",$pfxCertOb …
          |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | You cannot call a method on a null-valued expression.
    
    
   [1]: http://New-Object:+Line+|+++43+|++…+CertObject=+New-Object+System.Security.Cryptography.X509Certificates.+…++++++|++++++++++++++++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++++++|+Exception+calling+".ctor"+with+"3"+argument(s):+"Array+may+not+be+empty+or+null.+(Parameter+'rawData')"+InvalidOperation:+Line+|+++48+|++++++[io.file]::WriteAllBytes(".\appservicecertificate.pfx",$pfxCertOb+…++++++|++++++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++++++|+You+cannot+call+a+method+on+a+null-valued+expression.


Any help would be much appreciated


azure-webapps-ssl-certificates
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

brtrach-MSFT avatar image
0 Votes"
brtrach-MSFT answered MikeEangelo-6600 published

The issue is because the $secret object is missing the SecretValueText property.


As explained in below Github discussion, the SecretValueText has been deprecated since Az verson 3.0.0

https://github.com/MicrosoftDocs/azure-docs/issues/64538


Resolution:

After modifying the script following this document: https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/key-vault/certificates/how-to-export-certificate.md#powershell, you can successfully get the SecretValueText. Below is the full script for the reference:

 Function Export-AppServiceCertificate
    
 {
    
     ###########################################################
    
     Param(
    
         [Parameter(Mandatory=$true,Position=1,HelpMessage="ARM Login Url")]
    
         [string]$loginId,
    
         [Parameter(Mandatory=$true,HelpMessage="Subscription Id")]
    
         [string]$subscriptionId,
    
         [Parameter(Mandatory=$true,HelpMessage="Resource Group Name")]
    
         [string]$resourceGroupName,
    
         [Parameter(Mandatory=$true,HelpMessage="Name of the App Service Certificate Resource")]
    
         [string]$name
    
     )
    
     ###########################################################
    
     
    
     Connect-AzAccount
    
     Set-AzContext -Subscription $subscriptionId
    
     ## Get the KeyVault Resource Url and KeyVault Secret Name were the certificate is stored
    
     $ascResource= Get-AzResource -ResourceId "/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.CertificateRegistration/certificateOrders/$name"
    
     $certProps = Get-Member -InputObject $ascResource.Properties.certificates[0] -MemberType NoteProperty
    
     $certificateName = $certProps[0].Name
    
     $keyVaultId = $ascResource.Properties.certificates[0].$certificateName.KeyVaultId
    
     $keyVaultSecretName = $ascResource.Properties.certificates[0].$certificateName.KeyVaultSecretName
    
     ## Split the resource URL of KeyVault and get KeyVaultName and KeyVaultResourceGroupName
    
     $keyVaultIdParts = $keyVaultId.Split("/")
    
     $keyVaultName = $keyVaultIdParts[$keyVaultIdParts.Length - 1]
    
     $keyVaultResourceGroupName = $keyVaultIdParts[$keyVaultIdParts.Length - 5]
    
     
    
     ## --- !! NOTE !! ----
    
     ## Only users who can set the access policy and has the the right RBAC permissions can set the access policy on KeyVault, if the command fails contact the owner of the KeyVault
    
     Set-AzKeyVaultAccessPolicy -ResourceGroupName $keyVaultResourceGroupName -VaultName $keyVaultName -UserPrincipalName $loginId -PermissionsToSecrets get
    
     Write-Host "Get Secret Access to account $loginId has been granted from the KeyVault, please check and remove the policy after exporting the certificate"
    
     ## Getting the secret from the KeyVault
    
     $secret = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $keyVaultSecretName
    
     #$cert = Get-AzKeyVaultCertificate -VaultName "ContosoKV01" -Name "TestCert01"
    
     #$secret = Get-AzKeyVaultSecret -VaultName "ContosoKV01" -Name $cert.Name
    
     $secretValueText = '';
    
     $ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)
    
     try {
    
         $secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
    
     } finally {
    
         [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
    
     }
    
     $secretByte = [Convert]::FromBase64String($secretValueText)
    
     $x509Cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
    
     $x509Cert.Import($secretByte, "", "Exportable,PersistKeySet")
    
     $type = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx
    
     
    
     #$pfxCertObject= New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @([Convert]::FromBase64String($secret.SecretValueText),"",[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
    
     $pfxPassword = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 50 | % {[char]$_})
    
     $pfxFileByte = $x509Cert.Export($type, $pfxPassword)
    
     $currentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
    
     [Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
    
     [io.file]::WriteAllBytes(".\appservicecertificate.pfx",$x509Cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12,$pfxPassword))
    
     ## --- !! NOTE !! ----
    
     ## Remove the Access Policy required for exporting the certificate once you have exported the certificate to prevent giving the account prolonged access to the KeyVault
    
     ## The account will be completely removed from KeyVault access policy and will prevent to account from accessing any keys/secrets/certificates on the KeyVault,
    
     ## Run the following command if you are sure that the account is not used for any other access on the KeyVault or login to the portal and change the access policy accordingly.
    
     # Remove-AzKeyVaultAccessPolicy -ResourceGroupName $keyVaultResourceGroupName -VaultName $keyVaultName -UserPrincipalName $loginId
    
     # Write-Host "Access to account $loginId has been removed from the KeyVault"
    
     # Print the password for the exported certificate
    
     Write-Host "Created an App Service Certificate copy at: $currentDirectory\appservicecertificate.pfx"
    
     Write-Warning "For security reasons, do not store the PFX password. Use it directly from the console as required."
    
     Write-Host "PFX password: $pfxPassword"
    
 }


· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

FWIW running the above now presents different exceptions/warnings in 2022:

MethodInvocationException: 
Line |
  50 |     $x509Cert.Import($secretByte, "", "Exportable,PersistKeySet")
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Exception calling "Import" with "3" argument(s): "X509Certificate is immutable on this platform. Use the equivalent constructor instead."
MethodInvocationException: 
Line |
  55 |     $pfxFileByte = $x509Cert.Export($type, $pfxPassword)
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Exception calling "Export" with "2" argument(s): "Error occurred during a cryptographic operation."
MethodInvocationException: 
Line |
  58 |     [io.file]::WriteAllBytes(".\appservicecertificate.pfx",$x509Cert.E …
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Exception calling "Export" with "2" argument(s): "Error occurred during a cryptographic operation."
WARNING: We have migrated the API calls for this cmdlet from Azure Active Directory Graph to Microsoft Graph.
Visit https://go.microsoft.com/fwlink/?linkid=2181475 for any permission issues.
0 Votes 0 ·

This is really really funny as Azure links directly to the now broken content. This seems like a very obvious and embarrassing oversight:

0 Votes 0 ·
MikeEangelo-6600 avatar image
0 Votes"
MikeEangelo-6600 answered

The provided answers were riddled with problems for me. One of which it seems an attempt is made to twice: one for Pfx and one for Pkcs12. This didn't generate a pfx file for me. The following did:

Function Export-AppServiceCertificate
{
   ###########################################################
   Param(
       [Parameter(Mandatory=$true,Position=1,HelpMessage="ARM Login Url")]
       [string]$loginId,
       [Parameter(Mandatory=$true,HelpMessage="Subscription Id")]
       [string]$subscriptionId,
       [Parameter(Mandatory=$true,HelpMessage="Resource Group Name")]
       [string]$resourceGroupName,
       [Parameter(Mandatory=$true,HelpMessage="Name of the App Service Certificate Resource")]
       [string]$name
   )
   ###########################################################

  #this wasn't necessary for me:
   #Connect-AzAccount
   #Set-AzContext -Subscription $subscriptionId

    ## Get the KeyVault Resource Url and KeyVault Secret Name were the certificate is stored
   
   $ascResource= Get-AzResource -ResourceId "/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.CertificateRegistration/certificateOrders/$name"
   $certProps = Get-Member -InputObject $ascResource.Properties.certificates[0] -MemberType NoteProperty
   $certificateName = $certProps[0].Name
   $keyVaultId = $ascResource.Properties.certificates[0].$certificateName.KeyVaultId
   $keyVaultSecretName = $ascResource.Properties.certificates[0].$certificateName.KeyVaultSecretName
   
   ## Split the resource URL of KeyVault and get KeyVaultName and KeyVaultResourceGroupName
   $keyVaultIdParts = $keyVaultId.Split("/")
   $keyVaultName = $keyVaultIdParts[$keyVaultIdParts.Length - 1]
   $keyVaultResourceGroupName = $keyVaultIdParts[$keyVaultIdParts.Length - 5]
   
   ## --- !! NOTE !! ----
   ## Only users who can set the access policy and has the the right RBAC permissions can set the access policy on KeyVault, if the command fails contact the owner of the KeyVault
   Set-AzKeyVaultAccessPolicy -ResourceGroupName $keyVaultResourceGroupName -VaultName $keyVaultName -UserPrincipalName $loginId -PermissionsToSecrets get
   Write-Host "Get Secret Access to account $loginId has been granted from the KeyVault, please check and remove the policy after exporting the certificate"
   
   ## Getting the secret from the KeyVault
   $secret = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $keyVaultSecretName
   $secretValueText = '';
   $ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)
   
   try {
       $secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
   } finally {
       [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
   }
   
   $secretByte = [Convert]::FromBase64String($secretValueText)
   $x509Cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2($secretByte, "", "Exportable,PersistKeySet")
   $type = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx
   
   #$pfxCertObject= New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @([Convert]::FromBase64String($secret.SecretValueText),"",[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
   $pfxPassword = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 50 | % {[char]$_})
   $export = $x509Cert.Export($type, $pfxPassword)
   $currentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
   $destination = "$currentDirectory/appservicecertificate.pfx"
   [io.file]::WriteAllBytes($destination, $export)
   
   ## --- !! NOTE !! ----
   ## Remove the Access Policy required for exporting the certificate once you have exported the certificate to prevent giving the account prolonged access to the KeyVault
   ## The account will be completely removed from KeyVault access policy and will prevent to account from accessing any keys/secrets/certificates on the KeyVault,
   ## Run the following command if you are sure that the account is not used for any other access on the KeyVault or login to the portal and change the access policy accordingly.
   Remove-AzKeyVaultAccessPolicy -ResourceGroupName $keyVaultResourceGroupName -VaultName $keyVaultName -UserPrincipalName $loginId
   Write-Host "Access to account $loginId has been removed from the KeyVault"
   # Print the password for the exported certificate
   Write-Host "Created an App Service Certificate copy at: $destination"
   Write-Warning "For security reasons, do not store the PFX password. Use it directly from the console as required."
   Write-Host "PFX password: $pfxPassword"
}

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.