title: How to fix Access Denied while reading X509Certificate2
body: <p>I am creating an API to login to Xero via Private Applications, so I followed their instructions and Xero API documentation to see how it works. Link <a href="https://developer.xero.com/documentation/auth-and-limits/private-applications" rel="nofollow noreferrer">here</a>. </p>
  
  <p>So I created an App, and generate a public certificate (.cer file) using OpenSSL, and also created '.pfx file' for the same, and attached that file in Dynamics CRM in Notes entity.</p>
  
  <p>Now when I run my C# code to login using the same public/private-key pair it's working as expected. (The private/public-key pair were retrieved from CRM) and passed as expected to the login code (code has been pasted below)</p>
  
  <p>My next step was to create these public/private-key pair programmatically using C#, and I used the below code to create it.</p>
  
  <pre class="lang-cs prettyprint-override"><code>// Generate a RSA Key
  RSA RSAKey = RSA.Create(1024);
  
  // Creating Certificate Request with relevant parameters
  var req = new CertificateRequest(subjectNameDN, RSAKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
  
  // Creating self-signed certificate with an expiry defined in app settings
  X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(expiryDurationInYears));
  </code></pre>
  
  <p>subjectNameDN variable contains my OrganizationName, OrganizationUnitName, and CommonName in string format.</p>
  
  <p>After this step, what I do is, I create a .pfx file, and a .zip file(zip file contains the .cert file in it) and attach both of these to note records in Dynamics CRM.</p>
  
  <p>Below is the code</p>
  
  <p>Create .pfx file and attach to note in CRM:</p>
  
  <pre><code>Entity annotationPfx = new Entity("annotation");
  annotationPfx.Attributes["subject"] = "public_privatekey pfx";
  annotationPfx.Attributes["filename"] = "public_privatekey.pfx";
  annotationPfx.Attributes["documentbody"] = Convert.ToBase64String(cert .Export(X509ContentType.Pfx, myPassword));
  annotationPfx.Attributes["mimetype"] = "application/x-pkcs12";
  annotationPfx.Attributes["isdocument"] = true;
  _crmServiceClient.Create(annotationPfx);
  </code></pre>
  
  <p>Create zip file with .pfx file in it, and attach to note in CRM:</p>
  
  <pre><code> var certVal = "-----BEGIN CERTIFICATE-----" + Environment.NewLine + Convert.ToBase64String(cert .Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks) + Environment.NewLine + "-----END CERTIFICATE-----";
   byte[] certificateInBytes = System.Text.Encoding.UTF8.GetBytes(certVal);
   byte[] compressedCertInBytes;
   string certFileName = "MyCertificate.cer";
   using (var outStream = new MemoryStream())
      {
         using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
         {
             var fileInArchive = archive.CreateEntry(certFileName, CompressionLevel.Optimal);
             using (var entryStream = fileInArchive.Open())
             using (var fileToCompressStream = new MemoryStream(certificateInBytes))
              {
                  fileToCompressStream.CopyTo(entryStream);
              }
          }
              compressedCertInBytes = outStream.ToArray();
      }
  
   Entity annotationZip = new Entity("annotation");
   annotationZip.Attributes["subject"] = "publickey zip";
   annotationZip.Attributes["filename"] = "publickey.zip";                       
   annotationZip.Attributes["documentbody"] = Convert.ToBase64String(compressedCertInBytes);
   annotationZip.Attributes["mimetype"] = "application/zip";
   annotationZip.Attributes["isdocument"] = true;
  </code></pre>
  
  <p>After this, what I do is, I download the zip file, extract it, and paste the same in my Xero's Private Application.</p>
  
  <p>Then, I run my code to login to Xero(pasted below)</p>
  
  <pre><code>var baseApiUrl = "https://api.xero.com";
  var consumerKey = myConsumerKey;
  var privateConsumer = new Consumer(consumerKey, consumerSecret);
  
  certificate = new X509Certificate2(Convert.FromBase64String(retrieveBase64FromCRM), 
               myPassword, 
               X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
  
  var privateAuthenticator = new PrivateAuthenticator(certificate);
  
  var privateApplicationSettings = new ApplicationSettings
  {
     BaseApiUrl = baseApiUrl,
     Consumer = privateConsumer,
     Authenticator = privateAuthenticator
   };
  
  ApplicationSettings applicationSettings = privateApplicationSettings;
  
  if (applicationSettings.Authenticator is IAuthenticator)
  {
                  IXeroCoreApi myXeroApi = new XeroCoreApi(applicationSettings.BaseApiUrl, applicationSettings.Authenticator as IAuthenticator,
                  applicationSettings.Consumer, User(), new DefaultMapper(), new DefaultMapper());
  }
  
  string getXeroOrgName = myXeroApi != null &amp;&amp; !string.IsNullOrEmpty(myXeroApi.Organisation.Name.ToString()) ? myXeroApi.Organisation.Name.ToString() : string.Empty;
  </code></pre>
  
  <p>This code is where the trouble occurs. I get the below error.</p>
  
  <p>Error when I pass the parameters 'X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable' to get the certificate</p>
  
  <p><a href="https://i.stack.imgur.com/zHhex.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/zHhex.png" alt="Access Denied"></a></p>
  
  <p>I get the error Access Denied.</p>
  
  <p>Stack Trace:-</p>
  
  <pre><code>    System.Security.Cryptography.CryptographicException: Access denied.
  
     at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
     at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle&amp; pCertCtx)
     at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
     at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)
     at myFunctionName(Dictionary`2 myParameters)
  </code></pre>
  
  <p>Without these parameters, the error I get is</p>
  
  <p><a href="https://i.stack.imgur.com/4kYKH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/4kYKH.png" alt="Invalid provider type specified"></a></p>
  
  <p>Stack Trace:</p>
  
  <pre><code>System.Security.Cryptography.CryptographicException: Invalid provider type specified.
  
        at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
  at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle&amp; safeProvHandle, SafeKeyHandle&amp; safeKeyHandle)
  at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
  at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
  at Xero.Api.Infrastructure.ThirdParty.Dust.RsaSha1.Sign(SHA1CryptoServiceProvider hash)
  at Xero.Api.Infrastructure.ThirdParty.Dust.RsaSha1.SignCore(String baseString)
  at Xero.Api.Infrastructure.OAuth.Signing.RsaSha1Signer.CreateSignature(X509Certificate2 certificate, IToken token, Uri uri, String verb, String verifier, Boolean renewToken, String callback)
  at Xero.Api.Example.Applications.Private.PrivateAuthenticator.GetSignature(IConsumer consumer, IUser user, Uri uri, String verb, IConsumer consumer1)
  at Xero.Api.Infrastructure.Http.HttpClient.CreateRequest(String endPoint, String method, String accept, String query)
  at Xero.Api.Infrastructure.Http.HttpClient.Get(String endpoint, String query)
  at Xero.Api.Infrastructure.Http.XeroHttpClient.Get[TResult,TResponse](String endPoint)
  at Xero.Api.Common.XeroReadEndpoint`3.Get(String endpoint, String child)
  at Xero.Api.Common.XeroReadEndpoint`3.Find()
  at Xero.Api.Core.XeroCoreApi.get_Organisation()
  at myFunctionName(Dictionary`2 myParameters)
  </code></pre>
  
  <p>But to my surprise, when I hosted this API on Azure, it's working as expected with no issues, and I am able to login to Xero, but I can't debug it on my local machine. </p>
  
  <p>I am able to debug the code if I use the values of the certificate created using OpenSSL, but if I use values of the ones created programmatically, I get error.</p>
  
  <p>Any help on this would be appreciated.</p>