2013-08-20 14 views
13

मेरे पास एक स्व-होस्टेड डब्ल्यूसीएफ सर्वर है जो स्थानीय सिस्टम खाते के तहत विंडोज सेवा के रूप में चल रहा है। मैं संदेश स्तर सुरक्षा का उपयोग कर नेट.tcp एंडपॉइंट के साथ उपयोग के लिए सी # में प्रोग्रामेटिक रूप से एक स्व-हस्ताक्षरित प्रमाणपत्र बनाने की कोशिश कर रहा हूं।डब्ल्यूसीएफ सेवा के लिए प्रोग्रामेटिक रूप से स्व-हस्ताक्षरित प्रमाणपत्र कैसे बनाएं?

मैं निम्नलिखित कोड का उपयोग कर रहा हूं जो How to create a self-signed certificate using C#? में स्वीकृत उत्तर के आधार पर मेरी समस्या को हल करने की कोशिश कर रहे कुछ छोटे बदलावों के साथ बहुत करीब से है।

public static X509Certificate2 CreateSelfSignedCertificate(string subjectName, TimeSpan expirationLength) 
{ 
    // create DN for subject and issuer 
    var dn = new CX500DistinguishedName(); 
    dn.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE); 

    CX509PrivateKey privateKey = new CX509PrivateKey(); 
    privateKey.ProviderName = "Microsoft Strong Cryptographic Provider"; 
    privateKey.Length = 1024; 
    privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; 
    privateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG | X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG; 
    privateKey.MachineContext = true; 
    privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG; 
    privateKey.Create(); 

    // Use the stronger SHA512 hashing algorithm 
    var hashobj = new CObjectId(); 
    hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, 
     ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 
     AlgorithmFlags.AlgorithmFlagsNone, "SHA1"); 

    // Create the self signing request 
    var cert = new CX509CertificateRequestCertificate(); 
    cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, ""); 
    cert.Subject = dn; 
    cert.Issuer = dn; // the issuer and the subject are the same 
    cert.NotBefore = DateTime.Now.Date; 
    // this cert expires immediately. Change to whatever makes sense for you 
    cert.NotAfter = cert.NotBefore + expirationLength; 
    //cert.X509Extensions.Add((CX509Extension)eku); // add the EKU 
    cert.HashAlgorithm = hashobj; // Specify the hashing algorithm 
    cert.Encode(); // encode the certificate 

    // Do the final enrollment process 
    var enroll = new CX509Enrollment(); 
    enroll.InitializeFromRequest(cert); // load the certificate 
    enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name 
    string csr = enroll.CreateRequest(); // Output the request in base64 
    // and install it back as the response 
    enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate, 
     csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password 
    // output a base64 encoded PKCS#12 so we can import it back to the .Net security classes 
    var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption 
     PFXExportOptions.PFXExportChainWithRoot); 

    // instantiate the target class with the PKCS#12 data (and the empty password) 
    return new System.Security.Cryptography.X509Certificates.X509Certificate2(
     System.Convert.FromBase64String(base64encoded), "", 
     // mark the private key as exportable (this is usually what you want to do) 
     // mark private key to go into the Machine store instead of the current users store 
     X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet 
    ); 
} 

और मैं यह इस कोड के साथ की दुकान:

X509Store store = new X509Store(storeName, StoreLocation.LocalMachine); 
store.Open(OpenFlags.ReadWrite); 
store.Add(newCert); 
store.Close(); 

यह प्रमाणपत्र बनाता है और LocalMachine प्रमाणपत्र संग्रह में कहते हैं। समस्या यह है कि जब मैं डब्ल्यूसीएफ सेवा शुरू करने का प्रयास करता हूं तो मुझे निम्नलिखित अपवाद मिलता है:

यह संभावना है कि प्रमाणपत्र 'सीएन = myCertificate' में एक निजी कुंजी नहीं हो सकती है जो कुंजी विनिमय करने में सक्षम हो या प्रक्रिया हो निजी कुंजी के लिए अधिकारों का उपयोग। विस्तार के लिए कृपया आंतरिक अपवाद देखें। इनर अपवाद: कुंजी समुच्चय

मौजूद नहीं है मेरी प्रमाण पत्र के लिए FindPrivateKey नमूना (http://msdn.microsoft.com/en-us/library/aa717039%28v=vs.100%29.aspx) के उत्पादन में है:

Private key directory: 
C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys 
Private key file name: 
f0d47c7826b8ef5148b6d412f1c40024_4a8a026f-58e4-40f7-b779-3ae9b6aae1a7 

मैं एक्सप्लोरर में इस 1.43KB फ़ाइल देख सकते हैं। अगर मैं गुणों को देखता हूं। सुरक्षा मैं पूर्ण नियंत्रण के साथ सिस्टम और प्रशासक दोनों को देखता हूं।

इस त्रुटि की खोज में मैंने निजी कुंजी गायब या गलत अनुमतियों के बारे में कई उत्तरों को देखा है। मैं नहीं देख सकता कि समस्या क्या है।

वास्तव में अजीब बात यह है कि अगर मैं एमएमसी प्रमाणपत्र प्लगइन का उपयोग करता हूं, तो प्रमाण पत्र पर जाएं और सभी कार्य चुनें। निजी कुंजी प्रबंधित करें ... मुझे वही सुरक्षा सेटिंग्स दिखाई देती हैं। इसे देखने के बाद भी अगर मैं सिर्फ संवाद लाता हूं और रद्द करें बटन दबाता हूं तो प्रमाण पत्र अब डब्ल्यूसीएफ में सही तरीके से काम करता है। मैं बस सेवा को पुनरारंभ कर सकता हूं और सबकुछ पूरी तरह से चलता है।

यदि मैं मेकर्ट का उपयोग करके प्रमाण पत्र बनाता हूं तो यह शुरुआत से ठीक काम करता है। मुझे नहीं पता कि यह अलग-अलग क्या करता है।

जानकारी का एक अन्य टुकड़ा प्रासंगिक नहीं हो सकता है कि प्रमाण पत्र न केवल मेरे स्टोर में रखा जाता है जहां मैंने इसे रखने के लिए कहा था, लेकिन इसे "इंटरमीडिएट सर्टिफिकेशन अथॉरिटीज" स्टोर में भी रखा जाता है। मुझे नहीं पता क्यों या क्यों मायने रखता है।

तो ... कोई विचार क्या मैं गलत कर रहा हूं?

अद्यतन: ठीक है, यह सिर्फ एक डब्ल्यूसीएफ मुद्दा नहीं है। जब मैं HttpSetServiceConfiguration का उपयोग कर http.sys के साथ एंडपॉइंट पर बाध्य करने के लिए प्रमाणपत्र का उपयोग करने का प्रयास करता हूं तो मुझे अनिवार्य रूप से वही समस्या मिलती है। विधि 1312 लौटाती है - "एक निर्दिष्ट लॉगऑन सत्र मौजूद नहीं है। इसे पहले ही समाप्त कर दिया जा सकता है"। यह वास्तव में असली त्रुटि नहीं है। मैं सुरक्षा इवेंट में देखा था लॉग इन करें एक लेखापरीक्षा विफलता है कि इस कहते हैं:

Cryptographic Parameters: 
    Provider Name: Microsoft Software Key Storage Provider 
    Algorithm Name: Not Available. 
    Key Name: {A23712D0-9A7B-4377-89DB-B1B39E3DA8B5} 
    Key Type: Machine key. 

Cryptographic Operation: 
    Operation: Open Key. 
    Return Code: 0x80090011 

0x80090011 वस्तु नहीं मिला था है। तो यह एक ही समस्या प्रतीत होता है। फिर, प्रमाणपत्र के लिए निजी कुंजी प्रबंधित करें संवाद खोलने के बाद यह पूरी तरह से काम करता है।

मैं अभी भी समस्या का कारण ढूंढ रहा हूं।

अद्यतन # 2: मैं नीचे दिए गए स्वीकृत उत्तर का उपयोग करके यह काम करने में सक्षम था। दिलचस्प बात यह है कि अब यह कोड X509Store कोड को कॉल किए बिना मशीन स्टोर में प्रमाण पत्र डालता प्रतीत होता है। मैं अभी भी कोड को कॉल करता हूं क्योंकि मुझे यकीन नहीं है और इससे कुछ भी नुकसान नहीं पहुंचाता है। यहां अंतिम कोड है जिसका उपयोग मैं प्रमाण पत्र बनाने के लिए कर रहा हूं।

static public X509Certificate2 CreateSelfSignedCertificate(string subjectName, TimeSpan expirationLength) 
    { 
     // create DN for subject and issuer 
     var dn = new CX500DistinguishedName(); 
     dn.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE); 

     CX509PrivateKey privateKey = new CX509PrivateKey(); 
     privateKey.ProviderName = "Microsoft Strong Cryptographic Provider"; 
     privateKey.Length = 2048; 
     privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; 
     privateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG | X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG; 
     privateKey.MachineContext = true; 
     privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG; 
     privateKey.Create(); 

     // Use the stronger SHA512 hashing algorithm 
     var hashobj = new CObjectId(); 
     hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, 
      ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 
      AlgorithmFlags.AlgorithmFlagsNone, "SHA512"); 

     // Create the self signing request 
     var cert = new CX509CertificateRequestCertificate(); 
     cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, ""); 
     cert.Subject = dn; 
     cert.Issuer = dn; // the issuer and the subject are the same 
     cert.NotBefore = DateTime.Now.Date; 
     // this cert expires immediately. Change to whatever makes sense for you 
     cert.NotAfter = cert.NotBefore + expirationLength; 
     cert.HashAlgorithm = hashobj; // Specify the hashing algorithm 
     cert.Encode(); // encode the certificate 

     // Do the final enrollment process 
     var enroll = new CX509Enrollment(); 
     enroll.InitializeFromRequest(cert); // load the certificate 
     enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name 
     string csr = enroll.CreateRequest(); // Output the request in base64 
     // and install it back as the response 
     enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate, 
      csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password 
     // output a base64 encoded PKCS#12 so we can import it back to the .Net security classes 
     var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption 
      PFXExportOptions.PFXExportChainWithRoot); 

     // instantiate the target class with the PKCS#12 data (and the empty password) 
     return new System.Security.Cryptography.X509Certificates.X509Certificate2(
      System.Convert.FromBase64String(base64encoded), "", 
      // mark the private key as exportable (this is usually what you want to do) 
      // mark private key to go into the Machine store instead of the current users store 
      X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet 
     ); 
    } 
+0

क्या कोई जानता है कि किसी मौजूदा प्रमाणपत्र को दोस्ताना नाम से कैसे लोड किया जाए? –

+0

अद्यतन प्रदान करने के लिए धन्यवाद .. मेरे पास एक समान समस्या थी और मैं यह गलत नहीं कर सका कि मैं क्या कर रहा था, आपने इस पोस्ट के साथ मुझे बहुत मदद की! :) – Spyral

उत्तर

5

मुझे PowerShell में समकक्ष कोड का उपयोग करके एक ही समस्या थी। ऐसा लगता है कि कभी-कभी निजी कुंजी गायब हो जाती है। मैंने प्रोसेस मॉनीटर का इस्तेमाल किया और आप हटाए जा रहे कुंजी फाइल को देख सकते हैं।

जिस तरह से मैंने इसे हल किया था, वह X509KeyStorageFlags.PersistKeySet को X509Certificate2 कन्स्ट्रक्टर में जोड़ना था।

+0

उत्तर के लिए धन्यवाद। मेरे पास एक कामकाजी समाधान है और मैं इसे बदलने के लिए नाराज हूं। मेरे पास अब इस जवाब का परीक्षण करने का समय नहीं है, लेकिन यदि मैं करता हूं, तो मैं वापस आऊंगा और इसे स्वीकार किए गए उत्तर के रूप में चिह्नित करूंगा। – MarkR

+0

एक साल बाद, मैंने आखिरकार इस जवाब की जांच की और यह काम करता है! मैंने निर्माता को PersistKeySet ध्वज जोड़ा और कुंजी गायब नहीं होती है। मैंने वर्किंग कोड दिखाने के लिए अपनी मूल पोस्ट अपडेट की। – MarkR

5

मैं यह काम नहीं कर सका, लेकिन मुझे एक वैकल्पिक समाधान मिला। (दिसंबर 2014 अपडेट करें: अब मुझे स्वीकार्य उत्तर का उपयोग करके काम करने के लिए मिल गया है।)

मैं जो चाहता हूं उसे प्राप्त करने के लिए PluralSight.Crypto library का उपयोग करने में सक्षम था। स्थानीयमाचिन स्टोर में स्टोर करने के लिए निजी कुंजी प्राप्त करने के लिए मुझे थोड़ा सा स्रोत कोड संशोधित करना पड़ा। मेरे द्वारा किए गए परिवर्तन CryptContext.cs फ़ाइल में थे। मैंने CreateSelfSignedCertificate विधि को बदल दिया है। मेरे द्वारा किए गए परिवर्तन सहित कोड का एक स्निपेट निम्नलिखित है। संक्षेप में, मैंने CryptKeyProviderInformation संरचना के ध्वज सदस्य को 0x20 (CRYPT_MACHINE_KEYSET) पर सेट करने के लिए सेट किया है यदि CryptContext ऑब्जेक्ट में यह मान अपने ध्वज में है।

 byte[] asnName = properties.Name.RawData; 
     GCHandle asnNameHandle = GCHandle.Alloc(asnName, GCHandleType.Pinned); 

     int flags = 0;     // New code 
     if ((this.Flags & 0x20) == 0x20) // New code 
      flags = 0x20;     // New code 

     var kpi = new Win32Native.CryptKeyProviderInformation 
     { 
      ContainerName = this.ContainerName, 
      KeySpec = (int)KeyType.Exchange, 
      ProviderType = 1, // default RSA Full provider 
      Flags = flags     // New code 
     }; 

तो मैं इस तरह अपने खुद के कोड में समारोह का उपयोग करें:

 using (Pluralsight.Crypto.CryptContext ctx = new Pluralsight.Crypto.CryptContext()) { 

      ctx.Flags = 0x8 | 0x20; 
      ctx.Open(); 

      X509Certificate2 cert = ctx.CreateSelfSignedCertificate(
       new Pluralsight.Crypto.SelfSignedCertProperties 
       { 
        IsPrivateKeyExportable = true, 
        KeyBitLength = 4096, 
        Name = new X500DistinguishedName("CN=" + subjectName), 
        ValidFrom = DateTime.Today, 
        ValidTo = DateTime.Today + expirationLength, 
       }); 

      return cert; 
     } 

सूचना है कि मैं CryptContext वस्तु के लिए झंडे सेट 0x8 होने के लिए | 0x20 (CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)।

मेरी इच्छा है कि मैं यह समझ सकूं कि मेरे मूल समाधान में क्या गलत था। लेकिन मुझे काम करने के लिए कुछ चाहिए और मेरे परीक्षण में यह समाधान मुझे चाहिए जो मुझे चाहिए। मुझे उम्मीद है कि यह किसी और के साथ रास्ते में मदद करता है।

+0

मुझे इन CryptContext ध्वज कहां मिल सकता है? मुझे कोई दस्तावेज नहीं मिल रहा है। –

+0

आप यहां फ्लैग के लिए प्रलेखन पा सकते हैं: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379886%28v=vs.85%29.aspx और संख्यात्मक ध्वज मान यहां: http : //www.pinvoke.net/default.aspx/advapi32.cryptacquirecontext – MarkR

3

आप कोडप्लेक्स (https://clrsecurity.codeplex.com/) पर सीएलआर सुरक्षा लाइब्रेरी का भी उपयोग कर सकते हैं। यहां नमूना कोड है जो स्वयं हस्ताक्षरित प्रमाणपत्र बनाता है, और इसे SSLStream के साथ परीक्षण करता है।

 var machineName = Environment.MachineName; 
     var keyCreationParameters = new CngKeyCreationParameters(); 
     keyCreationParameters.KeyUsage = CngKeyUsages.AllUsages; 
     keyCreationParameters.KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey; 
     keyCreationParameters.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(4096), CngPropertyOptions.None)); 
     var cngKey = CngKey.Create(CngAlgorithm2.Rsa, "Test", keyCreationParameters); 

     var x500DistinguishedName = new X500DistinguishedName("CN=" + machineName); 
     x500DistinguishedName.Oid.Value = "1.3.6.1.5.5.7.3.1"; 
     var certificateCreationParameters = new X509CertificateCreationParameters(x500DistinguishedName); 
     certificateCreationParameters.SignatureAlgorithm = X509CertificateSignatureAlgorithm.RsaSha512; 
     certificateCreationParameters.TakeOwnershipOfKey = true; 
     certificateCreationParameters.CertificateCreationOptions = X509CertificateCreationOptions.None; 
     certificateCreationParameters.EndTime = new DateTime(9999, 12,31, 23, 59, 59, 999, DateTimeKind.Utc); 
     var certificate = cngKey.CreateSelfSignedCertificate(certificateCreationParameters); 

     var certificateStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser); 
     certificateStore.Open(OpenFlags.ReadWrite); 
     certificateStore.Add(certificate); 
     certificateStore.Close(); 


     var tcpListener = TcpListener.Create(6666); 
     tcpListener.Start(); 
     var client = new TcpClient("localhost", 6666); 
     var acceptedClient = tcpListener.AcceptTcpClient(); 
     var acceptedClinetSslStream = new SslStream(
      acceptedClient.GetStream(), false); 
     var serverAuthTask = acceptedClinetSslStream.AuthenticateAsServerAsync(certificate, 
          false, SslProtocols.Tls, true); 

     SslStream clientSslStream = new SslStream(
      client.GetStream(), 
      false, 
      delegate(object o, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors errors) 
       { 
        if (errors == SslPolicyErrors.None) 
         return true; 

        Console.WriteLine("Certificate error: {0}", errors); 

        // Do not allow this client to communicate with unauthenticated servers. 
        return false; 
       }, 
      null); 
     var clientAuthTask = clientSslStream.AuthenticateAsClientAsync(machineName); 

     Task.WaitAll(serverAuthTask, clientAuthTask); 
संबंधित मुद्दे