Skip to content

Subject order and missing DC attributes when signing intermediate CA with smallstep (Microsoft CA compatibility issue) #1606

Open

Description

@JannikZorn

Hello!

  • Vote on this issue by adding a 👍 reaction
  • If you want to implement this feature, comment to let us know (we'll work with you on design, scheduling, etc.)

Issue details

Hi everyone,

I’m currently trying to use a smallstep root CA to sign a Microsoft Windows CA as a subordinate CA, but I’m running into issues with the subject field handling.

Here’s my setup:

I created a CSR for the Microsoft CA.
Then I signed it using my smallstep root CA with:

step certificate sign \ --profile intermediate-ca \ intermediate-ms.csr \ /etc/step-ca/certs/root_ca.crt \ /etc/step-ca/secrets/root_ca_key

Initially, the resulting certificate was missing required extensions like:

CDP (CRL Distribution Point)
AIA (Authority Information Access)

So I created a custom template:

{ "subject": {{ toJson .Insecure.CR.Subject }}, "sans": {{ toJson .SANs }}, "issuingCertificateURL": "http://xxx/root.crt", {{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} "keyUsage": ["keyEncipherment", "digitalSignature"], {{- else }} "keyUsage": ["digitalSignature"], {{- end }} "extKeyUsage": ["serverAuth", "clientAuth"], "basicConstraints": { "isCA": true, "maxPathLen": 0 }, "crlDistributionPoints": [ "http://xxx/root.crl" ] }

With this template, the extensions are now correct and accepted.

However, I’m facing a new issue with the subject:

In my CSR (.req), the subject looks like this:

CN=abc, DC=def, DC=ghi

When I use:

"subject": {{ toJson .Insecure.CR.Subject }}

the subject in the issued certificate becomes:

DC=ghi, DC=def, CN=abc

→ so all attributes are present, but the order is reversed.
If I instead use:

"subject": {{ toJson .Subject }}

then the resulting certificate only contains:

CN=abc

which makes sense because the template overrides the subject and only includes the CN.

Problem:
Microsoft CA seems to reject the certificate when the subject order is changed, even though the same attributes are present.

According to the documentation and discussions (e.g. issue #718), I expected .Insecure.CR.Subject to preserve the subject exactly as defined in the CSR — including the order — but that does not seem to be the case.

Questions:

Is the reordering of subject attributes when using .Insecure.CR.Subject expected behavior?
Is there any way to preserve the exact original subject order from the CSR?
Has anyone successfully used smallstep to sign a Microsoft subordinate CA certificate without running into this issue?

Any help would be greatly appreciated — I’m currently stuck on this.

Thanks!

Image

Why is this needed?

The .Insecure.CR.Subject Option in templates doesn't work like it should.

Activity

  1. hslatman commented on Apr 2, 2026

    @hslatman
    Member

    Hey @JannikZorn,

    I'm not aware of other users running into this issue. Generally people are going the other way while migrating away from or in co-operation with ADCS: signing an intermediate off of ADCS, and use that with step-ca. What is your use case, exactly?

    It seems this may be some quirk with ADCS, potentially doing a form of strict string comparison on the subject. We don't have much time to look into this now ourselves, but we'd be open to contributions, assuming the changes are not too invasive.

  2. JannikZorn commented on Apr 9, 2026

    @JannikZorn
    Author

    Hi everyone,

    quick update on my original question and also a bit more context on our use case.

    We are moving away from Microsoft Active Directory Certificate Services completely and switch fully to Smallstep CA. However, due to the Microsoft auto-enrollment feature (for laptops and Windows servers), we still need to keep the Microsoft CA in place, at least for now. That’s why the goal is to run the Microsoft CA as a subordinate CA under Smallstep.

    Current Status

    Unfortunately, I was not able to fully solve the issue within Smallstep.

    However, I did find some interesting behavior:

    When using the --csr profile, the subject order is preserved correctly
    When signing the CSR without any template, the subject order is also correct
    But: in that case, I cannot add required extensions like AIA and CDP

    As soon as I use:

    a custom template (any template), or
    the --profile intermediate-ca

    the order of the subject attributes (CN, DC, etc.) gets rearranged.

    So the behavior seems to be:

    ✅ Correct order → only when NOT using templates
    ❌ Reordered subject → whenever a template or intermediate profile is used
    Workaround (using OpenSSL)

    As a workaround, I ended up signing the CSR using OpenSSL instead of Smallstep.

    This allows preserving the subject order and adding the required extensions.

    The downside: I had to set up a separate OpenSSL “database” (text file–based CA structure).

    1. Create directory structure
    mkdir -p /etc/step-ca/microsoft-ca/{certs,crl,newcerts,private}
    cd /etc/step-ca/microsoft-ca
    
    1. Create “database”
    touch microsoft-ca-db
    echo 1000 > serial
    echo 1000 > crlnumber
    
    1. Create config (microsoft-ca.cnf)
    [ ca ]
    default_ca = CA_default
    
    [ CA_default ]
    dir               = /etc/step-ca/microsoft-ca
    database          = $dir/microsoft-ca-db
    new_certs_dir     = $dir/newcerts
    certificate       = /etc/step-ca/certs/root_ca.crt
    private_key       = /etc/step-ca/secrets/root_ca_key
    serial            = $dir/serial
    crlnumber         = $dir/crlnumber
    
    default_md        = sha256
    policy            = policy_loose
    x509_extensions   = v3_intermediate_ca
    default_days      = 3650
    
    copy_extensions   = copy
    
    [ policy_loose ]
    commonName = supplied
    
    [ v3_intermediate_ca ]
    basicConstraints = critical, CA:true, pathlen:0
    keyUsage = critical, digitalSignature, keyCertSign, cRLSign
    
    authorityInfoAccess = @aia
    crlDistributionPoints = @crl
    
    [aia]
    caIssuers;URI.0 = http://xxx/root.crt
    
    [crl]
    URI.0 = http://xxx/root.crl
    
    1. Provide CSR
    cp new.csr /etc/step-ca/microsoft-ca/
    
    1. Sign certificate
    openssl ca \
      -config /etc/step-ca/microsoft-ca/microsoft-ca.cnf \
      -in new.csr \
      -out new.crt \
      -batch \
      -preserveDN
    
    1. Verify result
    openssl req -in request.req -noout -subject -nameopt RFC2253
    openssl x509 -in new.crt -noout -subject -nameopt RFC2253
    openssl x509 -in new.crt -noout -text
    

    Check database:

    cat /etc/step-ca/microsoft-ca/microsoft-ca-db

    1. If you need to recreate the cert
    grep -v "/CN=i-tracing-Germany-SubCA" microsoft-ca-db.txt > microsoft-ca-db.txt.tmp
    mv microsoft-ca-db.txt.tmp microsoft-ca-db.txt
    

    Additional Notes

    After signing with OpenSSL, I extracted:

    • the CRL
    • the public root certificate

    from the root CA and made them available via a separate HTTP endpoint, since the root CA itself is offline.

    Conclusion

    So in summary:

    Smallstep works fine without templates, but then lacks required extensions
    As soon as templates are used, the subject order changes, which breaks compatibility with Microsoft CA
    The only working solution for me so far is using OpenSSL with -preserveDN

    If anyone has managed to solve this cleanly within Smallstep, I’d still be very interested in hearing about it.

    Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementneeds triageWaiting for discussion / prioritization by team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions