<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://tech.uvoo.io/index.php?action=history&amp;feed=atom&amp;title=MSCA_certsrv</id>
	<title>MSCA certsrv - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://tech.uvoo.io/index.php?action=history&amp;feed=atom&amp;title=MSCA_certsrv"/>
	<link rel="alternate" type="text/html" href="https://tech.uvoo.io/index.php?title=MSCA_certsrv&amp;action=history"/>
	<updated>2026-05-10T18:43:13Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.2</generator>
	<entry>
		<id>https://tech.uvoo.io/index.php?title=MSCA_certsrv&amp;diff=5579&amp;oldid=prev</id>
		<title>Busk: Created page with &quot;``` import argparse import base64 import re import time import requests from requests_ntlm import HttpNtlmAuth from cryptography import x509 from cryptography.x509.oid import...&quot;</title>
		<link rel="alternate" type="text/html" href="https://tech.uvoo.io/index.php?title=MSCA_certsrv&amp;diff=5579&amp;oldid=prev"/>
		<updated>2025-07-08T21:08:04Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;``` import argparse import base64 import re import time import requests from requests_ntlm import HttpNtlmAuth from cryptography import x509 from cryptography.x509.oid import...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;```&lt;br /&gt;
import argparse&lt;br /&gt;
import base64&lt;br /&gt;
import re&lt;br /&gt;
import time&lt;br /&gt;
import requests&lt;br /&gt;
from requests_ntlm import HttpNtlmAuth&lt;br /&gt;
from cryptography import x509&lt;br /&gt;
from cryptography.x509.oid import NameOID&lt;br /&gt;
from cryptography.hazmat.primitives import serialization, hashes&lt;br /&gt;
from cryptography.hazmat.primitives.asymmetric import rsa&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def generate_key_and_csr(common_name, san_list=None):&lt;br /&gt;
    # Generate a private key&lt;br /&gt;
    key = rsa.generate_private_key(&lt;br /&gt;
        public_exponent=65537,&lt;br /&gt;
        key_size=2048,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    # Build subject&lt;br /&gt;
    subject = x509.Name([&lt;br /&gt;
        x509.NameAttribute(NameOID.COMMON_NAME, common_name),&lt;br /&gt;
    ])&lt;br /&gt;
&lt;br /&gt;
    # Build CSR&lt;br /&gt;
    csr_builder = x509.CertificateSigningRequestBuilder().subject_name(subject)&lt;br /&gt;
    if san_list:&lt;br /&gt;
        san = x509.SubjectAlternativeName([&lt;br /&gt;
            x509.DNSName(dns) for dns in san_list&lt;br /&gt;
        ])&lt;br /&gt;
        csr_builder = csr_builder.add_extension(san, critical=False)&lt;br /&gt;
&lt;br /&gt;
    csr = csr_builder.sign(key, hashes.SHA256())&lt;br /&gt;
    csr_pem = csr.public_bytes(serialization.Encoding.PEM).decode('utf-8')&lt;br /&gt;
    return key, csr_pem&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def submit_csr(ca_url, csr_pem, template, auth):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Submit a CSR to the Microsoft CA Web Enrollment service.&lt;br /&gt;
    Uses Mode=newreq, CertRequest and CertAttrib form fields. ([stackoverflow.com](https://stackoverflow.com/questions/31283476/submitting-base64-csr-to-a-microsoft-ca-via-curl))&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    session = requests.Session()&lt;br /&gt;
    session.auth = auth&lt;br /&gt;
    # Initialize session (get cookies)&lt;br /&gt;
    session.get(f&amp;quot;{ca_url}/certrqxt.asp&amp;quot;, verify=False)&lt;br /&gt;
&lt;br /&gt;
    data = {&lt;br /&gt;
        'Mode': 'newreq',&lt;br /&gt;
        'CertRequest': csr_pem,&lt;br /&gt;
        'CertAttrib': f'CertificateTemplate:{template}',&lt;br /&gt;
        'FriendlyType': '',&lt;br /&gt;
        'TargetStoreFlags': '0',&lt;br /&gt;
        'SaveCert': 'yes'&lt;br /&gt;
    }&lt;br /&gt;
    resp = session.post(f&amp;quot;{ca_url}/certfnsh.asp&amp;quot;, data=data, verify=False)&lt;br /&gt;
    resp.raise_for_status()&lt;br /&gt;
&lt;br /&gt;
    # Extract the Request ID from the response&lt;br /&gt;
    match = re.search(r&amp;quot;certnew\.cer\?ReqID=(\d+)&amp;amp;&amp;quot;, resp.text)&lt;br /&gt;
    if not match:&lt;br /&gt;
        raise RuntimeError(&amp;quot;Failed to obtain Request ID from CA response&amp;quot;)&lt;br /&gt;
    return match.group(1)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def retrieve_cert(ca_url, req_id, auth, timeout=60):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Retrieve issued certificate in DER from CA. ([certsrv.readthedocs.io](https://certsrv.readthedocs.io/en/latest/_modules/certsrv.html?utm_source=chatgpt.com))&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    session = requests.Session()&lt;br /&gt;
    session.auth = auth&lt;br /&gt;
    end_time = time.time() + timeout&lt;br /&gt;
    while time.time() &amp;lt; end_time:&lt;br /&gt;
        resp = session.get(&lt;br /&gt;
            f&amp;quot;{ca_url}/certnew.cer&amp;quot;, &lt;br /&gt;
            params={'ReqID': req_id, 'Enc': 'bin'},&lt;br /&gt;
            verify=False&lt;br /&gt;
        )&lt;br /&gt;
        if resp.headers.get('Content-Type') == 'application/pkix-cert':&lt;br /&gt;
            return resp.content&lt;br /&gt;
        time.sleep(2)&lt;br /&gt;
    raise TimeoutError(&amp;quot;Certificate not issued within timeout period&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def main():&lt;br /&gt;
    parser = argparse.ArgumentParser(&lt;br /&gt;
        description='Generate a key, CSR and submit to Microsoft CA Web Enrollment'&lt;br /&gt;
    )&lt;br /&gt;
    parser.add_argument('--ca-url', required=True,&lt;br /&gt;
                        help='Base URL of CA Web Enrollment (e.g. https://ca-server/certsrv)')&lt;br /&gt;
    parser.add_argument('--template', required=True,&lt;br /&gt;
                        help='Certificate template name')&lt;br /&gt;
    parser.add_argument('--username', required=True,&lt;br /&gt;
                        help='User in DOMAIN\\user format for NTLM auth')&lt;br /&gt;
    parser.add_argument('--password', required=True,&lt;br /&gt;
                        help='Password for NTLM auth')&lt;br /&gt;
    parser.add_argument('--cn', required=True,&lt;br /&gt;
                        help='Common Name for certificate subject')&lt;br /&gt;
    parser.add_argument('--san', nargs='*', default=None,&lt;br /&gt;
                        help='Optional Subject Alternative Names (DNS)')&lt;br /&gt;
    parser.add_argument('--output-key', default='private_key.pem',&lt;br /&gt;
                        help='Output path for private key (PEM)')&lt;br /&gt;
    parser.add_argument('--output-cert', default='certificate.pem',&lt;br /&gt;
                        help='Output path for certificate (PEM)')&lt;br /&gt;
    args = parser.parse_args()&lt;br /&gt;
&lt;br /&gt;
    auth = HttpNtlmAuth(args.username, args.password)&lt;br /&gt;
&lt;br /&gt;
    # Generate key and CSR&lt;br /&gt;
    key, csr_pem = generate_key_and_csr(args.cn, args.san)&lt;br /&gt;
&lt;br /&gt;
    # Save private key&lt;br /&gt;
    with open(args.output_key, 'wb') as f:&lt;br /&gt;
        f.write(&lt;br /&gt;
            key.private_bytes(&lt;br /&gt;
                encoding=serialization.Encoding.PEM,&lt;br /&gt;
                format=serialization.PrivateFormat.TraditionalOpenSSL,&lt;br /&gt;
                encryption_algorithm=serialization.NoEncryption()&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    print(f'Private key saved to {args.output_key}')&lt;br /&gt;
&lt;br /&gt;
    # Submit CSR&lt;br /&gt;
    req_id = submit_csr(args.ca_url.rstrip('/'), csr_pem, args.template, auth)&lt;br /&gt;
    print(f'Submitted CSR, Request ID = {req_id}')&lt;br /&gt;
&lt;br /&gt;
    # Retrieve issued certificate&lt;br /&gt;
    cert_der = retrieve_cert(args.ca_url.rstrip('/'), req_id, auth)&lt;br /&gt;
&lt;br /&gt;
    # Convert DER to PEM and save&lt;br /&gt;
    cert = x509.load_der_x509_certificate(cert_der)&lt;br /&gt;
    with open(args.output_cert, 'wb') as f:&lt;br /&gt;
        f.write(cert.public_bytes(serialization.Encoding.PEM))&lt;br /&gt;
    print(f'Certificate saved to {args.output_cert}')&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    main()&lt;br /&gt;
&lt;br /&gt;
```&lt;/div&gt;</summary>
		<author><name>Busk</name></author>
	</entry>
</feed>