2016-11-30 103 views
2

我遇到以下情況,感覺我已經耗盡了Google和Stack Overflow的各種研究渠道後,我決定只問自己關於它的問題。將生成的證書添加到商店並更新IIS網站綁定

我試圖根據我已擁有和擁有的CA證書生成個人證書(使用BouncyCastle)。生成證書後,將其放入「我的」商店,然後嘗試更新我的IIS網站的SSL綁定以使用此新證書。

我注意到IIS網站的更新(使用ServerManager)沒有拋出異常,但是當我轉到IIS管理器控制檯時,我注意到該網站的綁定沒有選擇SSL證書。當我嘗試選擇我創建的證書(顯示了罰款作爲一種可行的選擇),我收到以下錯誤信息:

A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)

作爲測試我出口我生成的證書(使用私鑰)和通過嚮導重新安裝它,然後再次嘗試設置綁定(通過IIS管理器)哪些工作。

由於這種行爲,我認爲這是我如何生成或添加證書到商店的問題。我希望有人可能對我所遇到的問題有所瞭解。以下是在創建證書時使用的相關功能,將其添加到商店並以編程方式更新網站的綁定:

主要功能生成獲取CA證書私鑰的私鑰,簽名的證書,並更新站點綁定:

public static bool GenerateServerCertificate(
    X509Certificate2 CACert, 
    bool addToStore, 
    DateTime validUntil) 
{ 
    try 
    { 
     if (CACert.PrivateKey == null) 
     { 
      throw new CryptoException("Authority certificate has no private key"); 
     } 

     var key = DotNetUtilities.GetKeyPair(CACert.PrivateKey).Private; 

     byte[] certHash = GenerateCertificateBasedOnCAPrivateKey(
      addToStore, 
      key, 
      validUntil); 

     using (ServerManager manager = new ServerManager()) 
     { 
      Site site = manager.Sites.Where(q => q.Name == "My Site").FirstOrDefault(); 

      if (site == null) 
      { 
       return false; 
      } 

      foreach (Binding binding in site.Bindings) 
      { 
       if (binding.Protocol == "https") 
       { 
        binding.CertificateHash = certHash; 
        binding.CertificateStoreName = "MY"; 
       } 
      } 

      manager.CommitChanges(); 
     } 
    } 
    catch(Exception ex) 
    { 
     LOG.Error("Error generating certitifcate", ex); 
     return false; 
    } 

    return true; 
} 

基礎上,CA私鑰生成證書:

public static byte[] GenerateCertificateBasedOnCAPrivateKey(
    bool addToStore, 
    AsymmetricKeyParameter issuerPrivKey, 
    DateTime validUntil, 
    int keyStrength = 2048) 
{ 
    string subjectName = $"CN={CertSubjectName}"; 

    // Generating Random Numbers 
    CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); 
    SecureRandom random = new SecureRandom(randomGenerator); 
    ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerPrivKey, random); 

    // The Certificate Generator 
    X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator(); 
    certificateGenerator.AddExtension(
     X509Extensions.ExtendedKeyUsage, 
     true, 
     new ExtendedKeyUsage((new List<DerObjectIdentifier> { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") }))); 

    // Serial Number 
    BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); 
    certificateGenerator.SetSerialNumber(serialNumber); 

    // Issuer and Subject Name    
    X509Name subjectDN = new X509Name(subjectName); 
    X509Name issuerDN = new X509Name(CACertificateName); 
    certificateGenerator.SetIssuerDN(issuerDN); 
    certificateGenerator.SetSubjectDN(subjectDN); 

    // Valid For 
    DateTime notBefore = DateTime.UtcNow.Date; 
    DateTime notAfter = validUntil > notBefore ? validUntil : notBefore.AddYears(1); 

    certificateGenerator.SetNotBefore(notBefore); 
    certificateGenerator.SetNotAfter(notAfter); 

    // Subject Public Key 
    AsymmetricCipherKeyPair subjectKeyPair; 
    var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength); 
    var keyPairGenerator = new RsaKeyPairGenerator(); 
    keyPairGenerator.Init(keyGenerationParameters); 
    subjectKeyPair = keyPairGenerator.GenerateKeyPair(); 

    certificateGenerator.SetPublicKey(subjectKeyPair.Public); 

    // Generating the Certificate 
    Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory); 

    // correcponding private key 
    PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); 

    // merge into X509Certificate2 
    X509Certificate2 x509 = new X509Certificate2(certificate.GetEncoded()); 

    Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(info.ParsePrivateKey().GetDerEncoded()); 
    if (seq.Count != 9) 
    { 
     throw new PemException("Malformed sequence in RSA private key"); 
    } 

    RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(seq); 
    RsaPrivateCrtKeyParameters rsaparams = new RsaPrivateCrtKeyParameters(
     rsa.Modulus, 
     rsa.PublicExponent, 
     rsa.PrivateExponent, 
     rsa.Prime1, 
     rsa.Prime2, 
     rsa.Exponent1, 
     rsa.Exponent2, 
     rsa.Coefficient); 

    x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams); 

    if (addToStore) 
    { 
     // Add certificate to the Personal store 
     AddCertToStore(x509, StoreName.My, StoreLocation.LocalMachine, "Certificate Friendly Name"); 
    } 

    return x509.GetCertHash(); 
} 

添加證書到店:

private static void AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation, string friendlyName) 
{ 
    X509Store store = new X509Store(storeName, storeLocation); 

    try 
    { 
     store.Open(OpenFlags.ReadWrite); 
     store.Add(cert); 

     if (!string.IsNullOrWhiteSpace(friendlyName)) { 
      var certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, cert.Subject, true); 
      if (certs.Count > 0) 
      { 
       certs[0].FriendlyName = friendlyName; 
      } 
     } 
    } 
    finally 
    { 
     store.Close(); 
    } 
} 

只是最後一點,我從我所看到各種網站上的關於該錯誤嘗試了一些東西(好像不是很清楚是什麼問題):

  • 這適用於不同的盒子(我的個人發展機),但我打了一個服務器計算機上這些斷枝(運行Windows Server 2012 R2)
  • IIS幫助對話框通知我機器運行IIS 8.5
  • 驗證所產生的效力證書和CA證書與CertUtil.exe
  • 驗證生成的證書和CA證書有可以找到的私鑰
  • 已驗證的管理員(甚至最終甚至是我的登錄帳戶)都有權訪問CA證書和生成的證書的私鑰文件。

任何想法我的問題可能是什麼?


更新:

我能夠做得到一些結果如下:

導出我的證書文件編程做File.WriteAllBytes(filePath, cert.Export(X509ContentType.Pkcs12, password));

然後我導入此證書文件到店做:

var cert = new X509Certificate2(certFilePath, certPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); 

// My original AddCertToStore function 
AddCertToStore(cert, StoreName.My, StoreLocation.LocalMachine, "Friendly Name"); 

最後,我設置的結合,因爲我是較早做:

using (ServerManager manager = new ServerManager()) 
{ 
    Site site = manager.Sites.Where(q => q.Name == "My Site").FirstOrDefault(); 

    if (site == null) 
    { 
     return false; 
    } 

    foreach (Binding binding in site.Bindings) 
    { 
     if (binding.Protocol == "https") 
     { 
      binding.CertificateHash = certHash; 
      binding.CertificateStoreName = "MY"; 
     } 
    } 

    manager.CommitChanges(); 
} 

這樣做,這樣的作品,但我不明白爲什麼我會在證書導出到一個文件,然後將其加載到X509Certificate2對象,添加到商店,最後設置綁定。

+0

我有類似的代碼相同的問題,並添加'X509KeyStorageFlags.MachineKeySet'無疑解決了這個問題。 –

回答

0

ToRSA方法最有可能產生一個短暫的RSA密鑰,所以當引用都不見了鑰匙被刪除。將臨時結構導出爲PFX,然後使用PersistKeySet重新導入它是將其轉換爲持久鍵的一種方法。還有一些是存在的,但那是一個不那麼複雜的。

不過,實際上並不需要將它寫入文件。

byte[] pkcs12Blob = cert.Export(X509ContentType.Pkcs12, password); 
ver certWithPersistedKey = new X509Certificate2(pkcs12Blob, password, allTheFlagsYouAlreadySet); 

還有其他的細微之處正在進行,如設置專用密鑰屬性具有從商店加載和一個用於證書例如不同的行爲,這是從字節加載中...的PFX/PKCS#12出口/導入適用於所有這些。

+0

謝謝,這是一個更好的解決方案。 – Fizz