dotnet/wpf · error · CryptographicException

SR.DigSigCannotLocateCertificate

Error message

SR.DigSigCannotLocateCertificate

What it means

GetPrivateKeyForSigning looks up the signer certificate by thumbprint in the certificate store when the provided X509Certificate2 lacks an accessible private key. If no matching certificate with a private key is found, CryptographicException(SR.DigSigCannotLocateCertificate) is thrown.

Solutions

  1. Install the certificate with its private key (import the PFX into CurrentUser\My via certmgr or Import-PfxCertificate).
  2. Pass an X509Certificate2 instance that already has PrivateKey set so the store lookup is skipped.
  3. Verify the private key exists: check cert.HasPrivateKey before signing.
  4. If the key is on a smart card/HSM, ensure the CSP/KSP is installed and the token is accessible to the running user.

Example fix

// before: public-only cert
X509Certificate2 cert = new X509Certificate2("signer.cer");
dsm.Sign(toSign, cert); // throws
// after: load PFX with private key
X509Certificate2 cert = new X509Certificate2("signer.pfx", pfxPassword, X509KeyStorageFlags.MachineKeySet);
dsm.Sign(toSign, cert);
Defensive patterns

Strategy: validation

Validate before calling

bool canSign = signer != null && signer.HasPrivateKey;
if (!canSign)
{
    using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadOnly);
    canSign = store.Certificates.Find(X509FindType.FindByThumbprint, signer.Thumbprint, true)
               .Count > 0;
}

Type guard

static bool PrivateKeyResolvable(X509Certificate2 c) =>
    c != null && (c.HasPrivateKey ||
    (new X509Store(StoreName.My, StoreLocation.CurrentUser) is var s) && false); // prefer explicit store check in caller code

Try / catch

try { dsm.Sign(toSign, cert); }
catch (CryptographicException ex) when (ex.Message.Contains("Locate"))
{ /* private key not installed — prompt user to import PFX */ }

Prevention

When it happens

Trigger: Calling PackageDigitalSignatureManager.Sign with a certificate loaded without its private key (e.g. from a .cer file or a PFX imported without the key), and no cert with that thumbprint plus a private key exists in the personal store.

Common situations: Deploying only the public certificate to servers; PFX password lost so the key was never imported; certificates on smart cards not reachable from the process account; app pool identities lacking access to the key (though that often surfaces as a different access error).

Understand the failure class

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/5b4606e885d84581. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlDigitalSignatureProcessor.cs:1137

            // look for appropriate certificates
            X509Store store = new X509Store(StoreLocation.CurrentUser);

            try
            {
                store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);

                X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;

                collection = collection.Find(X509FindType.FindByThumbprint, signer.Thumbprint, true);
                if (collection.Count > 0)
                {
                    if (collection.Count > 1)
                        throw new CryptographicException(SR.DigSigDuplicateCertificate);

                    signer = collection[0];
                }
                else
                    throw new CryptographicException(SR.DigSigCannotLocateCertificate);
            }
            finally
            {
                store.Close();
            }

            // get the corresponding AsymmetricAlgorithm
            return GetPrivateKey(signer);
        }


        /// <summary>
        /// This method validated the Reference tags as per the restrictions imposed
        /// by the OPC spec.
        /// NOTE: The same method is called from Verify and Sign methods. At verify time we need to make sure
        /// that there is exactly one Package-specific reference. At Sign time we need to make sure that
        /// there are no package-specific references in the list of references passed to Sign APIs as a 
        /// input parameter, since we will be generating Package-specific object.

View on GitHub (pinned to 81131a70a4)