DevOps · K8s · Volleyball · Travel  •  DevOps · K8s · Volleyball · Travel  •  DevOps · K8s · Volleyball · Travel
Explore NY Stream

Microsoft PKI Certificate Acquisition and Installation For Web Application Server with SAN Extensions with certutil instructions

— ny_wk

Microsoft PKI Certificate Acquisition and Installation For Web Application Server with SAN Extensions with certutil instructions

To request a SAN web server certificate from a Microsoft PKI (Active Directory Certificate Services) on a non-domain Windows server, you build an INF policy file, generate a CSR with certreq -new, submit it to the CA web enrollment site, then accept the issued certificate with certreq -accept. This guide walks the full lifecycle end to end, fixes the common mistakes that cause silent failures, and shows how to verify and export the result as a PFX.

The big advantage of the certreq approach is that it works the same whether or not the target machine is joined to the domain that hosts the Enterprise CA. That is exactly the situation a SAN web server certificate request usually lands in: a public-facing web application server in a DMZ that must trust an internal Microsoft PKI without being a member of it.

The problem: web servers need SANs, and the CN alone is no longer trusted

Modern browsers and TLS clients ignore the certificate Common Name (CN) entirely for hostname matching. Chrome dropped CN fallback in version 58 (2017), and OpenSSL, Java, and the .NET HttpClient followed. A certificate is only valid for the hostnames listed in its Subject Alternative Name (SAN) extension.

That means if your site answers on www.example.com and example.com, both names must appear as DNS entries in the SAN, even if one of them is also the CN. Leaving a name out of the SAN is the number-one cause of NET::ERR_CERT_COMMON_NAME_INVALID after an otherwise successful install.

Prerequisites before you request the certificate

  • The trusted root (and any intermediate) CA certificates must already be installed on the target server. On a non-domain machine these are not delivered by Group Policy, so you import them by hand. Without the full chain, the OS cannot validate the issued certificate and certreq -accept will fail.
  • You must be a local Administrator on the target server and run every command from an elevated Command Prompt or PowerShell. The private key is created in the machine key store, which a standard user cannot write to.
  • You need the exact certificate template name published on the CA, plus enrollment access. Template display names and internal names differ; you submit using the name the web enrollment site exposes.
  • Generate the request on the server that will host the certificate. Doing so keeps the private key on that machine and avoids ever transporting it.

Installing the root CA chain on a non-domain server

Download the chain from the CA web enrollment site (or get it from your security team), then import it into the machine stores:

  1. Open an elevated PowerShell and import the root into Trusted Root Certification Authorities:
    Import-Certificate -FilePath C:\PKI\root-ca.cer -CertStoreLocation Cert:\LocalMachine\Root
  2. Import any intermediate/issuing CA into Intermediate Certification Authorities:
    Import-Certificate -FilePath C:\PKI\issuing-ca.cer -CertStoreLocation Cert:\LocalMachine\CA
  3. Confirm the chain validates:
    certutil -verify -urlfetch C:\PKI\issuing-ca.cer

Some application stacks (Java keystores, Apache, certain appliances) maintain their own trust store separate from Windows. If your web application is one of those, the chain must be installed there too. Consult the application's documentation for its trust-store location.

Step 1 - Build the INF request file for your SAN web server certificate

The INF file is a plain-text policy that tells certreq what kind of key to create and which extensions to request. Use one of two templates depending on the trust direction you need.

Standard Server Authentication (one-way TLS)

Use this when the client validates the server but the server does not validate client certificates - the common case for a public website. Save it as WebServerStandardSAN.inf:

  1. [Version]
  2. Signature="$Windows NT$"
  3. [NewRequest]
  4. Subject = "CN=www.example.com"
  5. Exportable = TRUE
  6. KeyLength = 2048
  7. KeySpec = 1
  8. KeyUsage = 0xA0
  9. MachineKeySet = TRUE
  10. ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
  11. RequestType = PKCS10
  12. HashAlgorithm = SHA256
  13. [Extensions]
  14. 2.5.29.17 = "{text}"
  15. _continue_ = "dns=www.example.com&"
  16. _continue_ = "dns=example.com&"
  17. [RequestAttributes]
  18. CertificateTemplate = WebServer

Mutual Authentication (two-way / client + server TLS)

Use this when the certificate must also support client authentication, for example mutual TLS between application tiers. The file is identical except the template targets a mutual-auth template that includes both the Server Authentication and Client Authentication EKUs. Save it as WebServerMutualAuthenticationSAN.inf and change the final line to the mutual-auth template your CA publishes, e.g.:

  1. [RequestAttributes]
  2. CertificateTemplate = WebServerMutualAuth

What each INF setting actually does

SettingMeaning
SubjectThe certificate CN. Set it to the primary FQDN, and repeat that name in the SAN.
Exportable = TRUEAllows the private key to be exported later as a PFX. Set FALSE if the key must never leave the machine.
KeyLength = 2048RSA key size. 2048 is the modern minimum; 3072 or 4096 for longer-lived keys. 1024 is deprecated and rejected by public CAs.
KeySpec = 1AT_KEYEXCHANGE - required so the key can be used for TLS key exchange.
KeyUsage = 0xA0Digital Signature (0x80) + Key Encipherment (0x20), the correct usage for a TLS server certificate.
MachineKeySet = TRUEStores the key in the machine (computer) store, not the user store - essential for IIS and services.
HashAlgorithm = SHA256Signs the request with SHA-256. Always set this; SHA-1 is deprecated and untrusted.
2.5.29.17The OID for the Subject Alternative Name extension - this is where the real, trusted hostnames live.

A note on legacy values: older guides include EncipherOnly = FALSE with a comment that it is "only for Windows Server 2003 and Windows XP." Those operating systems are long past end of life - omit that line entirely on any supported Windows version. Similarly, never set KeyLength = 1024; it is below the accepted security floor.

Step 2 - Add every hostname and IP to the SAN extension

The SAN block is the part people get wrong. Each name is its own _continue_ line, each value ends with &, and the list must include the CN. To request an IP address SAN entry, use the ipaddress= prefix:

  1. [Extensions]
  2. 2.5.29.17 = "{text}"
  3. _continue_ = "dns=www.example.com&"
  4. _continue_ = "dns=example.com&"
  5. _continue_ = "dns=app1.example.com&"
  6. _continue_ = "ipaddress=10.20.30.40&"

Watch for these formatting traps:

  • The CN must be repeated as a DNS SAN. Clients do not read the CN, so a name only in the Subject is effectively invisible.
  • Use plain ASCII hostnames with no https:// prefix, no trailing slash, and no spaces.
  • Internal short names and IPs are usually fine for a private PKI, but a publicly trusted CA will reject IP SANs that are private addresses and will refuse internal-only names. This INF workflow is for an internal Microsoft Enterprise CA.

Step 3 - Generate the CSR with certreq -new

Open an elevated Command Prompt, change to the folder holding your INF, and run:

  1. cd C:\PKI\request
  2. certreq -new WebServerStandardSAN.inf server01.req

This creates the private key in the local machine key store and writes the Base64-encoded PKCS#10 request to server01.req. If you are on a non-domain machine you may see a warning that the template does not exist - that is expected and safe to ignore, because the template is validated by the CA at submission time, not locally.

Before submitting, sanity-check that the SAN landed correctly inside the request:

  1. certutil server01.req

Look for a Subject Alternative Name section in the output listing every DNS name you specified. If it is missing, your [Extensions] block has a syntax error - fix the INF and regenerate.

Step 4 - Submit the CSR to the CA web enrollment site

The signed certificate is issued by the Subordinate (Issuing) CA. Submit the CSR through the Microsoft Certificate Services web enrollment portal, typically at https://<ca-server>/certsrv:

  1. Browse to the web enrollment site from the target server and sign in with your enrollment credentials.
  2. Choose Request a certificate, then advanced certificate request.
  3. Select Submit a certificate request by using a base-64-encoded CMC or PKCS #10 file.
  4. Open server01.req in a text editor and paste its entire contents (including the -----BEGIN NEW CERTIFICATE REQUEST----- lines) into the Saved Request box.
  5. Select the correct Certificate Template from the dropdown - the standard web server template for one-way TLS, or the mutual-authentication template for two-way TLS.
  6. Click Submit.

If the template requires CA manager approval, you will get a Certificate Pending page with a Request ID. Record that ID and notify whoever administers the CA so they can approve and issue it.

Submitting from the command line instead

If the CA is reachable and you know its config string, you can skip the web UI entirely:

  1. certreq -submit -config "CASERVER\Issuing-CA" server01.req server01.cer

When approval is pending this returns a Request ID; retrieve the issued certificate later with certreq -retrieve <RequestID> server01.cer.

Step 5 - Download the certificate and full chain

Once the request is approved, return to the web enrollment site and choose View the status of a pending certificate request, then open your request:

  1. Select Base 64 encoded and click Download certificate. Save it as server01.cer in the same folder as your .req.
  2. Select Base 64 encoded again and click Download certificate chain. This gives you the issuing and root CA certificates needed for the chain of trust.

Base 64 (PEM) is the safe default. DER encoding is binary; only choose it if a specific application requires it.

Step 6 - Accept the certificate with certreq -accept

Copy server01.cer into the same directory as the original request, then bind it to the private key that was created in Step 3:

  1. cd C:\PKI\request
  2. certreq -accept server01.cer

This is the step that completes the request. certreq -accept takes the public certificate, matches it to the pending private key in the machine store, and installs the finished certificate into Local Computer > Personal > Certificates. The key and the certificate are now joined.

Common pitfalls and how to avoid them

  • "Cannot find object or property" on accept. The private key is gone. This happens if you regenerated the CSR (overwriting the pending key) or ran -new on a different machine. Re-run from the server where the original -new request was created.
  • Chain not trusted after install. The root and intermediate CA certificates are not in the machine Root/CA stores. Re-import them and re-run certutil -verify -urlfetch server01.cer.
  • Browser shows the cert but name is wrong. The hostname is not in the SAN. There is no fix but to request a new certificate with the correct SAN list - you cannot edit an issued certificate.
  • Key not exportable. You set Exportable = FALSE (or omitted it). Re-issue with Exportable = TRUE if you need a PFX.
  • Private key landed in the user store. You forgot MachineKeySet = TRUE, so IIS and Windows services cannot see the key. Always set it for server certificates.
  • Permission denied creating the request. The Command Prompt was not elevated. Right-click and Run as administrator.

Verification: confirm the SAN certificate is installed correctly

After accepting, verify three things: the certificate is in the right store, the SAN is correct, and the private key is present.

  1. List the machine personal store and confirm your certificate is there:
    certutil -store My
  2. Open the certificate (double-click it in certlm.msc > Personal > Certificates) and check the Details tab. The Subject Alternative Name field must list every hostname, and you should see the message "You have a private key that corresponds to this certificate" on the General tab.
  3. From PowerShell, confirm the private key binding programmatically:
    Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*example.com*" } | Select-Object Subject, HasPrivateKey, Thumbprint, DnsNameList
  4. If this is for IIS, bind it: in IIS Manager select the site, Edit Bindings, add an https binding, and pick your certificate from the dropdown. Then test the live endpoint:
    (Invoke-WebRequest https://www.example.com -UseBasicParsing).StatusCode

Optional: export the certificate with its private key as a PFX

To move the certificate to another server or back it up, export it as a password-protected PKCS#12 (.pfx) file. You can do this in the GUI or in one PowerShell command.

PowerShell (fastest)

  1. Capture the thumbprint of your certificate (from the verification step above).
  2. Export it:
    $pwd = ConvertTo-SecureString -String "StrongPassphrase!" -Force -AsPlainText
    Export-PfxCertificate -Cert Cert:\LocalMachine\My\<THUMBPRINT> -FilePath C:\PKI\server01.pfx -Password $pwd

MMC (GUI)

  1. Run certlm.msc to open Certificates for the Local Computer account.
  2. Under Personal > Certificates, right-click your certificate and choose All Tasks > Export.
  3. Select Yes, export the private key.
  4. Choose Personal Information Exchange - PKCS #12 (.PFX). Leave "Include all certificates in the certification path" unchecked unless the destination needs the full chain in the file.
  5. Protect it with a strong password (and store it safely - it cannot be recovered).
  6. Pick a path and filename, then click Finish.

Treat the resulting PFX like a credential: anyone with the file and password holds your server's private key.

Key Takeaways

  • Always generate the CSR on the server that will host it with certreq -new, so the private key never travels, and finish with certreq -accept on that same machine.
  • The SAN extension is what clients trust - repeat the CN as a DNS SAN and list every hostname and IP, because modern browsers ignore the Common Name.
  • Set MachineKeySet = TRUE, HashAlgorithm = SHA256, and at least KeyLength = 2048; drop legacy XP/2003-only INF lines.
  • Install the full root and intermediate chain first on non-domain servers, or both submission and acceptance will fail validation.
  • Verify before you trust it: check the SAN in the certificate details, confirm HasPrivateKey is true, then bind and test the HTTPS endpoint.

Frequently Asked Questions

What is the difference between certreq and certutil?

certreq manages the certificate request lifecycle - creating CSRs from INF files (-new), submitting them (-submit), retrieving issued certs (-retrieve), and installing them (-accept). certutil is a broader diagnostic and store-management tool used to inspect requests, verify chains (-verify -urlfetch), and dump store contents (-store). You use both together: certreq to obtain the certificate, certutil to inspect and verify it.

How do I add multiple domains to one Windows certificate?

List each domain as its own _continue_ = "dns=name&" line inside the 2.5.29.17 = "{text}" block in your INF file, including the name used as the CN. A single certificate can hold many SAN entries, which is exactly how you cover example.com, www.example.com, and any additional hostnames with one certificate.

Why does certreq -accept fail with "Cannot find the certificate request"?

The private key generated by certreq -new is missing on the machine where you ran -accept. Either you ran -new on a different computer, or you re-ran -new and overwrote the pending key. Run both -new and -accept on the same server, in the same session order, without regenerating the request in between.

Can I use a SAN certificate from a private Microsoft CA on the public internet?

Only clients that already trust your internal root CA will accept it - which is fine for internal apps, intranet sites, and mutual TLS between your own servers. For a public website that anonymous browsers must trust, you need a certificate from a publicly trusted CA instead, since visitors will not have your internal root installed.

For more Windows administration and SSL/TLS walkthroughs, subscribe on YouTube at @explorenystream.