2017-05-10 70 views
2

我正在研究一些證書籤名工具,我目前的目標是創建方法,該方法將使用現有證書作爲發行者生成新證書。下面是生成證書的方法:手動安裝證書和從代碼安裝有什麼區別?

public X509Certificate2 CreateCertificate(string subjectName, TimeSpan certificateLifespan, X509Certificate2 issuer = null) 
{ 
    var nameFormat = "CN={0}"; 

    var subject = string.Format(nameFormat, subjectName); 

    // create DN for subject and issuer 
    var dn = new CX500DistinguishedName(); 
    dn.Encode(subject); 

    var privateKey = new CX509PrivateKey 
    { 
     ProviderName = "Microsoft Strong Cryptographic Provider", 
     Length = 2048, 
     KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE, 
     KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG | 
          X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG, 
       MachineContext = true, 
       ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG 
    }; 

    privateKey.Create(); 

    var hashobj = new CObjectId(); 
    hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, 
       ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 
       AlgorithmFlags.AlgorithmFlagsNone, "SHA512"); 

    var cert = new CX509CertificateRequestCertificate(); 
    cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, ""); 
    cert.Subject = dn; 
    if (issuer == null) 
     cert.Issuer = dn; 
    else 
    { 
     var signerCertificate = new CSignerCertificate(); 
     signerCertificate.Initialize(true, X509PrivateKeyVerify.VerifyNone, EncodingType.XCN_CRYPT_STRING_HEXRAW, issuer.GetRawCertDataString()); 
       cert.SignerCertificate = signerCertificate; 
    } 
    cert.NotBefore = DateTime.Now.Date; 
    cert.NotAfter = cert.NotBefore + certificateLifespan; 
    cert.HashAlgorithm = hashobj; 
    cert.Encode(); 

    var enroll = new CX509Enrollment(); 
    enroll.InitializeFromRequest(cert); 
    enroll.CertificateFriendlyName = subjectName; 

    var csr = enroll.CreateRequest(); 
    enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate, 
       csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); 

    //InstallResponse automatically installs certificate to My store. We should remove it, and manage it manually. 
    using(var store = new X509Store(StoreName.My, StoreLocation.LocalMachine)) 
    { 
     store.Open(OpenFlags.ReadWrite); 

     var certificate = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(
        c => c.Subject.StartsWith(subject, StringComparison.Ordinal)); 

     if (certificate != null) 
      store.Remove(certificate); 
     store.Close(); 
    } 

    //Self-signed certificates are also authomatically installed to Intermediate Authority store 
    if(issuer == null) 
    { 
     using (var store = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine)) 
     { 
      store.Open(OpenFlags.ReadWrite); 

      var certificate = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(
         c => c.Subject.StartsWith(subject, StringComparison.Ordinal)); 

      if (certificate != null) 
       store.Remove(certificate); 
      store.Close(); 
     } 
    } 

    var base64encoded = enroll.CreatePFX("", PFXExportOptions.PFXExportChainWithRoot); 

    return new X509Certificate2(Convert.FromBase64String(base64encoded), "", 
       X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); 
} 

的事情是,它完美的作品不錯,但只有當我手動安裝根證書,或使用證書管理器。但我試圖從代碼中嵌入資源安裝證書。

這裏是我的方法來提取嵌入證書流:

protected Stream ExtractResourceStream(string embeddedResourcePath) 
{ 
    var assembly = GetType().Assembly; 

    var pathConverted = embeddedResourcePath.Replace("\\", "."); 

    var matchingResources = assembly.GetManifestResourceNames() 
      .Where(n => n.EndsWith(pathConverted, StringComparison.InvariantCultureIgnoreCase)) 
      .ToArray(); 

    var resource = matchingResources.FirstOrDefault(); 
    if (resource == null) 
     throw new InvalidOperationException(string.Format("Resource {0} not found.", embeddedResourcePath)); 
    if (matchingResources.Length > 1) 
     throw new InvalidOperationException(string.Format("Resource {0} found more than once.", embeddedResourcePath)); 

    return assembly.GetManifestResourceStream(resource); 
} 

然後我初始化它未來的方式:

var bytes = new BinaryReader(stream).ReadBytes((int)stream.Length); 
var root = new X509Certificate2(bytes, RootCertificatePassword); 

這裏是我的證書安裝方法:

private void AddCertificateToStore(X509Certificate2 certificate, StoreName storeName) 
{ 
    using (var store = new X509Store(storeName, StoreLocation.LocalMachine)) 
    { 
     store.Open(OpenFlags.ReadWrite); 
     store.Add(certificate); 
     store.Close(); 
    } 
} 

根證書似乎安裝正確,但我收到異常「找不到密鑰」在生成第二個證書時調用signerCertificate.Initialize

我正在使用受密碼保護的PFX根證書。

所以,我的問題是 - 手動安裝證書和從代碼流安裝它有什麼區別?我在代碼中安裝時遺漏了一些部分?

回答

0

嗯,那很愚蠢。顯然,我錯過了正確的鍵盤。它的工作原理,如果我初始化這樣的根證書:

var root = new X509Certificate2(bytes, RootCertificatePassword, 
       X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);