Create Enroll on Behalf of Another User Request
Applies To: Windows Server 2008
Create a request on behalf of another user by using an enrollment agent certificate. First, find the enrollment agent certificate by opening the user certificate store and searching the extended key usages (EKUs) for the Enrollment Agent OID.
After the enrollment agent certificate has been found (and validated), the inner request is then created based on a template name. This is followed by creating an outer request from the inner request, and finally, adding the RequesterName and a signer certificate to the request.
C#:
string EKUOid = "2.5.29.37";
string EnrollmentAgentOid = "1.3.6.1.4.1.311.20.2.1";
X509Store store = new X509Store("My", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = null;
foreach (X509Certificate2 c in store.Certificates)
{
// Check that there is a private key for the enrollment agent and also that it not expired.
// This does not check for revocation or for a valid chain etc.
if (c.HasPrivateKey && c.NotBefore <= DateTime.UtcNow && DateTime.UtcNow <= c.NotAfter )
{
foreach (X509Extension ext in c.Extensions)
{
if (ext.Oid.Value == EKUOid)
{
X509EnhancedKeyUsageExtension eku = (X509EnhancedKeyUsageExtension)ext;
foreach (Oid myusage in eku.EnhancedKeyUsages)
{
if (myusage.Value == EnrollmentAgentOid)
{
cert = c;
break;
}
}
}
}
}
}
if (cert == null)
{
Console.WriteLine("Cannot find enrollment agent certificate");
return null;
}
// Create a PKCS 10 inner request.
CX509CertificateRequestPkcs10Class pkcs10_req = new CX509CertificateRequestPkcs10Class();
pkcs10_req.InitializeFromTemplateName( X509CertificateEnrollmentContext.ContextUser, myTemplate );
// Create a CMC outer request and initialize
CX509CertificateRequestCmcClass cmc_req = new CX509CertificateRequestCmcClass();
cmc_req.InitializeFromInnerRequest(pkcs10_req);
cmc_req.RequesterName = OtherUser;
CSignerCertificateClass signer = new CSignerCertificateClass();
signer.Initialize(false, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_BASE64, Convert.ToBase64String(cert.GetRawCertData()));
cmc_req.SignerCertificate = signer;
// encode the request
cmc_req.Encode();
C++:
IX509CertificateRequestPkcs10*pkcs10_req = NULL;
IX509CertificateRequestCmc*cmc_req = NULL;
ISignerCertificate*signer = NULL;
HRESULT hr = S_OK;
LPSTRpszOIDs[1]={szOID_ENROLLMENT_AGENT};
CERT_ENHKEY_USAGEstCertUsage = {1,pszOIDs};
HCERTSTORE hStore = NULL;
hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"MY" );
if ( hStore == NULL )
{
_tprintf( _T("CertOpenStore: 0x%x\n"), GetLastError());
goto error;
}
// Simple check only for the Enrollment Agent EKU in a certificate. This does not perform a chain or policy check.
PCCERT_CONTEXT pcert = CertFindCertificateInStore( hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ENHKEY_USAGE, &stCertUsage, NULL );
if ( pcert == NULL )
{
_tprintf( _T("CertFindCertificateInStore: 0x%x\n"), GetLastError()); goto error;
}
hr = CoCreateInstance(
__uuidof(CX509CertificateRequestPkcs10),
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
__uuidof(IX509CertificateRequestPkcs10),
(void **) &pkcs10_req);
_JumpIfError(hr, error, _T("CoCreateInstance CX509CertificateRequestPkcs10"));
hr = pkcs10_req->InitializeFromTemplateName( ContextUser, myTemplate );
_JumpIfError(hr, error, _T("pkcs10_req->InitializeFromTemplateName"));
// Create a CMC outer request and initialize
hr = CoCreateInstance(
__uuidof(CX509CertificateRequestCmc),
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
__uuidof(IX509CertificateRequestCmc),
(void **) &cmc_req);
_JumpIfError(hr, error, _T("CoCreateInstance CX509CertificateRequestCmc"));
hr = cmc_req->InitializeFromInnerRequest(pkcs10_req);
_JumpIfError(hr, error, _T("cmc_req->InitializeFromInnerRequest"));
hr = cmc_req->put_RequesterName( OtherUser );
_JumpIfError(hr, error, _T("cmc_req->put_RequesterName"));
hr = CoCreateInstance(
__uuidof(CSignerCertificate),
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
__uuidof(ISignerCertificate),
(void **) &signer);
_JumpIfError(hr, error, _T("CoCreateInstance CSignerCertificate"));
BSTR BinCert = SysAllocStringLen( NULL, pcert->cbCertEncoded );
memcpy( BinCert, pcert->pbCertEncoded, pcert->cbCertEncoded );
hr = signer->Initialize(false, VerifyAllowUI, XCN_CRYPT_STRING_BINARY, BinCert );
_JumpIfError(hr, error, _T("signer->Initialize"));
hr = cmc_req->put_SignerCertificate( signer );
_JumpIfError(hr, error, _T("cmc_req->put_SignerCertificate"));
hr = cmc_req->Encode();
_JumpIfError(hr, error, _T("cmc_req->Encode"));
goto noerror;
error:
if (NULL != cmc_req ) cmc_req->Release();
noerror:
if ( NULL != signer ) signer->Release();
if ( NULL != pkcs10_req ) pkcs10_req->Release();
if ( hStore != NULL ) CertCloseStore( hStore, 0 );
VBScript:
Dim store
Set store = CreateObject( "CAPICOM.Store")
Store.Open CAPICOM_CURRENT_USER_STORE, "My", CAPICOM_STORE_OPEN_READ_ONLY OR CAPICOM_STORE_OPEN_EXISTING_ONLY
Dim cert, stat, eku, enrolcert
For each cert in Store.Certificates
Set stat = cert.IsValid
stat.EKU.OID = "1.3.6.1.4.1.311.20.2.1"
If stat.Result Then
For Each eku in Cert.ExtendedKeyUsage.EKUs
If eku.oid = "1.3.6.1.4.1.311.20.2.1" Then
enrolcert = cert.Export( CAPICOM_ENCODE_BASE64 )
End If
Next
End If
Next
Dim pkcs10_req
Set pkcs10_req = CreateObject( "X509Enrollment.CX509CertificateRequestPkcs10" )
pkcs10_req.InitializeFromTemplateName ContextUser, templatename
Dim cmc_req
Set cmc_req = CreateObject( "X509Enrollment.CX509CertificateRequestCmc" )
cmc_req.InitializeFromInnerRequest pkcs10_req
cmc_req.RequesterName = OtherUser
Dim signer
Set signer = CreateObject( "X509Enrollment.CSignerCertificate" )
signer.Initialize false, VerifyAllowUI, XCN_CRYPT_STRING_BASE64, enrolcert
cmc_req.SignerCertificate = signer
' encode the request
cmc_req.Encode
VB.NET:
Dim eku As X509EnhancedKeyUsageExtension
Dim myusage As Oid
Dim store As New X509Store("My", StoreLocation.CurrentUser)
store.Open(OpenFlags.ReadOnly)
Dim cert As X509Certificate2 = Nothing
Dim c As X509Certificate2
For Each c In store.Certificates
' Check that there is a private key for the enrollment agent and also that it not expired.
' This does not check for revocation or for a valid chain etc.
If c.HasPrivateKey And c.NotBefore <= DateTime.UtcNow And DateTime.UtcNow <= c.NotAfter Then
Dim ext As X509Extension
For Each ext In c.Extensions
If ext.Oid.Value = EKUOid Then
eku = ext
For Each myusage In eku.EnhancedKeyUsages
If myusage.Value = EnrollmentAgentOid Then
cert = c
Exit For
End If
Next
End If
Next
End If
Next
If (cert Is Nothing) Then
Return Nothing
End If
Dim pkcs10_req As New CX509CertificateRequestPkcs10Class
pkcs10_req.InitializeFromTemplateName(X509CertificateEnrollmentContext.ContextUser, myTemplate)
Dim cmc_req As New CX509CertificateRequestCmcClass
cmc_req.InitializeFromInnerRequest(pkcs10_req)
cmc_req.RequesterName = OtherUser
Dim signer As New CSignerCertificateClass
signer.Initialize(False, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_BASE64, Convert.ToBase64String(cert.GetRawCertData))
cmc_req.SignerCertificate = signer
cmc_req.Encode()