我一直在寻找各地(MSDN,Stackoverflow,博客等)来提出以下模式:
客户端向服务器请求与SMIME标准兼容的证书。 (缺少AD证书服务模板部分)
服务器接收PKCS10请求并对其进行修改,或使用API确保使用正确的模板注册
服务器生成响应并将其发送到客户端
客户接受回复并完成注册。
注意
即使我在Windows客户端使用.NET,我希望这个过程足够通用,以便有OSX的人可以使用内置钥匙串或OpenSSL申请证书并申请证书,不得不提到EKU中的证书模板。
我的目标是修改服务器上的请求并将结果发送回客户端。
这是我迄今为止所尝试过的。
class Program
{
private const int CC_DEFAULTCONFIG = 0;
private const int CC_UIPICKCONFIG = 0x1;
private const int CR_IN_BASE64 = 0x1;
private const int CR_IN_FORMATANY = 0;
private const int CR_IN_PKCS10 = 0x100;
private const int CR_DISP_ISSUED = 0x3;
private const int CR_DISP_UNDER_SUBMISSION = 0x5;
private const int CR_OUT_BASE64 = 0x1;
private const int CR_OUT_CHAIN = 0x100;
private static string requestText = "";
static string SMIMEEncrypt = "1.3.6.1.4.1.311.21.8.4946465.16405226.12930948.10533807.2139545.33.10793632.14573168";
static string SMIMESign = "1.3.6.1.4.1.311.21.8.4946465.16405226.12930948.10533807.2139545.33.5005369.11644649";
enum FullResponsePropertyPropID
{
FR_PROP_NONE =0,
FR_PROP_FULLRESPONSE =1,
FR_PROP_STATUSINFOCOUNT=2,
FR_PROP_BODYPARTSTRING=3,
FR_PROP_STATUS=4,
FR_PROP_STATUSSTRING=5,
FR_PROP_OTHERINFOCHOICE=6,
FR_PROP_FAILINFO=7,
FR_PROP_PENDINFOTOKEN=8,
FR_PROP_PENDINFOTIME=9,
FR_PROP_ISSUEDCERTIFICATEHASH=10,
FR_PROP_ISSUEDCERTIFICATE=11,
FR_PROP_ISSUEDCERTIFICATECHAIN=12,
FR_PROP_ISSUEDCERTIFICATECRLCHAIN=13,
FR_PROP_ENCRYPTEDKEYHAS=14,
FR_PROP_FULLRESPONSENOPKCS7=15,
FR_PROP_CAEXCHANGECERTIFICATEHASH=16,
FR_PROP_CAEXCHANGECERTIFICATE=17,
FR_PROP_CAEXCHANGECERTIFICATECHAIN=18,
FR_PROP_CAEXCHANGECERTIFICATECRLCHAIN=19,
FR_PROP_ATTESTATIONCHALLENGE=20,
FR_PROP_ATTESTATIONPROVIDERNAME=21
}
public static void Main()
{
int PROPTYPE_BINARY = 3;
//// Client
//var objEnroll = new CX509Enrollment();
//string RequestStr = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
////TEST SERVER
//CCertRequest CertRequest = new CCertRequest ();
// int Disposition = CertRequest.Submit(CR_IN_BASE64, RequestStr, string.Empty, @"caName");
string subjectName = "E= makerofthings7@me.com, CN = makerofthings7@me.com, OU = Technology, L= NYC, S= NY, C=US";
string friendlyName = "makerofthings7 - Encrypt...";
string templateName = SMIMEEncrypt;
// --------------
// --------------
// Client
// --------------
// --------------
// 1. Call any initialization method implemented by the IX509Enrollment object.
CX509Enrollment req1 = new CX509Enrollment();
req1.Initialize(X509CertificateEnrollmentContext.ContextUser);
// 2. Call the CreateRequest method.
string req1a = req1.CreateRequest();
// 3. Store the request for a period of time such as days or weeks.
// --------------
// --------------
// Server
// --------------
// --------------
// 4. Call the Initialize method to create a request object when you are ready to enroll.
var reqServer = new CX509CertificateRequestPkcs10();
reqServer.InitializeFromCertificate(
X509CertificateEnrollmentContext.ContextUser,
req1a // Beginning with Windows 7 and Windows Server 2008 R2, you can specify
// a certificate thumb print or serial number rather than an encoded certificate
);
// Add template (Sign or Encrypt or Both)
CX509ExtensionTemplateName tmplateData2 = new CERTENROLLLib.CX509ExtensionTemplateName();
tmplateData2.InitializeEncode(templateName);
var tmplatex5092 = (IX509Extension)tmplateData2;
reqServer.X509Extensions.Add(new CX509Extension(tmplatex5092));
// -----------------------------------------------------------------------------------
// The following properties can be set before calling the Encode method:
// AlternateSignatureAlgorithm
// ClientId
// HashAlgorithm
// ParentWindow
// RenewalCertificate
// Silent
reqServer.Silent = true;
// SuppressDefaults
// ContextMessage
//
// The following properties must be set, if at all, before calling the Encode method:
// CspInformations
// KeyContainerNamePrefix
// SmimeCapabilities
reqServer.SmimeCapabilities = true;
// Subject
CX500DistinguishedName objName2 = new CX500DistinguishedName();
objName2.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
reqServer.Subject = objName2;
// -----------------------------------------------------------------------------------
reqServer.Encode();
//reqServer.get_RawDataToBeSigned();
// InitializeFromRequest() does the following:
// Verifies that the request is a PKCS #10, PKCS #7, or CMC request object.
// Retrieves the template, if any, associated with the request.
// Validates the template.
// Sets the request object on the Request property.
// Retrieves the signature count, issuance policies, and application policies from the template.
// Retrieves the renewal certificate if one exists.
CX509Enrollment enroll2 = new CX509Enrollment();
enroll2.InitializeFromRequest(reqServer);
// Enroll()
// - The method may create a key pair if necessary. Depending on how you initialize the enrollment
// object and on what properties you set, there may be no need to create a key pair. For example,
// if you are renewing a certificate by using an existing key, or if the IX509PrivateKey object
// associated with the certificate request represents an existing key, this method does not create a new key pair.
enroll2.Enroll();
// ALTERNATE IMPLEMENTATION
//CERTCLILib.ICertRequest3 serverReq2 = new CCertRequest();
//var retVAl2 = serverReq2.Submit(CR_IN_ENCODEANY, reqr3.Request.get_RawData(), string.Empty, "a.issue01.bitclear.us\\Secure Issuer 01a-001");
//var serverSignedResponse = serverReq2.GetCertificate(CR_OUT_BASE64HEADER);
//var certwChain2 = serverReq2.GetCertificate(CR_OUT_BASE64HEADER | CR_OUT_CHAIN);
// --------------
// --------------
// Client
// --------------
// --------------
// 5.Populate the request object from your stored request.
CCertRequest CertRequest = new CCertRequest();
var disposition3 = CertRequest.RetrievePending(1, ""); // pretend we checked the server for result status, and it was OK
// If Issued...
if (true)
{
// 6. Call the InstallResponse method.
X509CertificateEnrollmentContext context = X509CertificateEnrollmentContext.ContextUser;
CX509Enrollment enroll3 = new CX509Enrollment();
string serverSignedResponse = CertRequest.GetFullResponseProperty(
(int) FullResponsePropertyPropID.FR_PROP_FULLRESPONSE, 0, PROPTYPE_BINARY, CR_OUT_BASE64).ToString();
enroll3.Initialize(context);
// InstallResponse()
// - Retrieves the dummy certificate from the external store.
// - Retrieves the certificate contained in the response and installs it on the computer.
// - Copies properties from the dummy certificate in the external store onto the newly installed certificate in the personal store.
enroll3.InstallResponse(
InstallResponseRestrictionFlags.AllowNone
| InstallResponseRestrictionFlags.AllowUntrustedRoot //Perform the same action as the AllowUntrustedCertificate flag
// but also installs the certificate even if the certificate chain
// cannot be built because the root is not trusted.
, serverSignedResponse, EncodingType.XCN_CRYPT_STRING_BASE64
, string.Empty // If there is a password, clear it from memory when you have finished using it by calling the SecureZeroMemory function
);
// CreatePFX()
// - Opens the certificate store in memory for the default provider.
// - Adds the installed certificate to the store or builds the certificate chain adds a link to it.
// - Exports the certificate and the private key to a PFX message depending on the export options specified.
// - Encodes the exported message by using DER.
string pfx = enroll3.CreatePFX(
"q", // When you have finished using the password, clear it from memory by calling the SecureZeroMemory function.
PFXExportOptions.PFXExportChainNoRoot);
}