When integrating Linux systems with Windows Certificate Authority (CA), many developers encounter issues with certificate template specification in CSRs. The core problem lies in how OpenSSL formats certificate template attributes versus what Windows CA expects.
The OpenSSL configuration shown attempts to include the certificate template through an attribute:
[ req_attributes ]
1.3.6.1.4.1.311.13.2.1 = CertificateTemplate=CustomUserOffline
While this appears in the CSR, Windows certutil fails to decode it properly due to format differences between OpenSSL's ASN.1 encoding and Windows' expected format.
Windows expects certificate template information in a specific ASN.1 format. Here's how to properly encode it in OpenSSL:
[ req_attributes ]
certificateTemplateName = ASN1:SEQUENCE:custom_template
[ custom_template ]
field1 = OID:1.3.6.1.4.1.311.21.7
field2 = EXPLICIT:0,UTF8:CustomUserOffline
Here's a fully functional OpenSSL configuration file:
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = v3_req
attributes = req_attributes
prompt = no
[ req_distinguished_name ]
C = FR
L = Paris
OU = IT
CN = FirstName LastName
[ req_attributes ]
certificateTemplateName = ASN1:SEQUENCE:custom_template
[ custom_template ]
field1 = OID:1.3.6.1.4.1.311.21.7
field2 = EXPLICIT:0,UTF8:CustomUserOffline
[ v3_req ]
keyUsage = digitalSignature,keyEncipherment
After generating the CSR with the corrected configuration, Windows certutil should now properly recognize the template information:
certutil -dump usercert.csr
The output should include the correctly decoded certificate template name without the "Cannot decode object" error.
For environments where you can use PowerShell, here's an alternative verification command:
$csr = [System.IO.File]::ReadAllBytes("usercert.csr")
$request = New-Object System.Security.Cryptography.X509Certificates.CertificateRequest -ArgumentList $csr
$request.CertificateExtensions | Where-Object {$_.Oid.Value -eq "1.3.6.1.4.1.311.21.7"}
Several issues may still arise:
- Ensure the template name exactly matches what's defined in the Windows CA
- Verify the CA has permissions to issue the specified template
- Check that the template supports the key specifications in your CSR
When submitting through C# certenroll API, ensure you're using the correct OID collection:
var objEnroll = new CX509Enrollment();
objEnroll.InitializeFromRequest(pkcs10Request);
objEnroll.CertificateFriendlyName = "Custom User Certificate";
objEnroll.Enroll();
When working with Windows Certificate Authorities, you often need to specify the certificate template directly in the CSR. While OpenSSL allows adding custom attributes through configuration files, Windows' certutil often fails to properly decode these attributes.
The Windows CA expects certificate template information in a specific format using OID 1.3.6.1.4.1.311.13.2.1 (Enrollment Name Value Pair). The issue occurs because:
- OpenSSL encodes the attribute as a simple string pair
- Windows expects a more complex ASN.1 structure
Here's a modified configuration file that produces Windows-compatible output:
[ req ]
default_bits = 2048
default_keyfile = usercert.key
distinguished_name = req_distinguished_name
attributes = req_attributes
req_extensions = req_ext
prompt = no
string_mask = utf8only
[ req_distinguished_name ]
C = FR
L = Paris
OU = IT
CN = FirstName LastName
[ req_attributes ]
certificateTemplateName = ASN1:PRINTABLESTRING:CustomUserOffline
[ req_ext ]
1.3.6.1.4.1.311.13.2.1 = ASN1:SEQUENCE:seq_sect
[ seq_sect ]
field1 = OID:1.3.6.1.4.1.311.21.7
field2 = UTF8:CustomUserOffline
Use this command to generate the CSR:
openssl req -new -key usercert.key -out usercert.csr \
-config usercert.cnf -extensions req_ext
To check if the CSR contains properly formatted attributes:
openssl asn1parse -in usercert.csr -i
You should see the OID and properly encoded sequence in the output.
If you're ultimately submitting via C#, you might consider generating the CSR directly with CertEnroll:
// C# example using CertEnroll
var objEnroll = new CX509Enrollment();
var objRequest = new CX509CertificateRequestPkcs10();
objRequest.InitializeFromPrivateKey(
X509CertificateEnrollmentContext.ContextUser,
new CX509PrivateKey(),
null);
objRequest.CertificateTemplate = new CX509CertificateTemplateADObject("CustomUserOffline");
objEnroll.InitializeFromRequest(objRequest);
string csr = objEnroll.CreateRequest();
For Linux clients needing Windows templates:
- Pre-generate the configuration files with correct OID formatting
- Use openssl ca with custom policies if you control the CA
- Consider using EST (Enrollment over Secure Transport) protocol
- Ensure the certificate template exists on the CA server
- Verify the requesting user has enroll permissions
- Check Windows Event Logs on the CA server for detailed errors
- Try viewing the CSR with different tools: certutil, certreq, or OpenSSL