2010-09-22 38 views
34

Chúng tôi đang cố gắng tạo chứng chỉ X509 (bao gồm cả khóa cá nhân) theo lập trình sử dụng thư viện C# và BouncyCastle. Chúng tôi đã thử sử dụng một số mã từ this sample by Felix Kollmann nhưng phần khóa cá nhân của chứng chỉ trả về giá trị rỗng. Mã và kiểm tra đơn vị như sau:Có thể tạo lập một chứng chỉ X509 bằng cách sử dụng chỉ C# không?

using System; 
using System.Collections; 
using Org.BouncyCastle.Asn1; 
using Org.BouncyCastle.Asn1.X509; 
using Org.BouncyCastle.Crypto; 
using Org.BouncyCastle.Crypto.Generators; 
using Org.BouncyCastle.Crypto.Prng; 
using Org.BouncyCastle.Math; 
using Org.BouncyCastle.Security; 
using Org.BouncyCastle.X509; 

namespace MyApp 
{ 
    public class CertificateGenerator 
    { 
     /// <summary> 
     /// 
     /// </summary> 
     /// <remarks>Based on <see cref="http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx"/></remarks> 
     /// <param name="subjectName"></param> 
     /// <returns></returns> 
     public static byte[] GenerateCertificate(string subjectName) 
     { 
      var kpgen = new RsaKeyPairGenerator(); 

      kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024)); 

      var kp = kpgen.GenerateKeyPair(); 

      var gen = new X509V3CertificateGenerator(); 

      var certName = new X509Name("CN=" + subjectName); 
      var serialNo = BigInteger.ProbablePrime(120, new Random()); 

      gen.SetSerialNumber(serialNo); 
      gen.SetSubjectDN(certName); 
      gen.SetIssuerDN(certName); 
      gen.SetNotAfter(DateTime.Now.AddYears(100)); 
      gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0))); 
      gen.SetSignatureAlgorithm("MD5WithRSA"); 
      gen.SetPublicKey(kp.Public); 

      gen.AddExtension(
       X509Extensions.AuthorityKeyIdentifier.Id, 
       false, 
       new AuthorityKeyIdentifier(
        SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public), 
        new GeneralNames(new GeneralName(certName)), 
        serialNo)); 

      gen.AddExtension(
       X509Extensions.ExtendedKeyUsage.Id, 
       false, 
       new ExtendedKeyUsage(new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") })); 

      var newCert = gen.Generate(kp.Private); 
      return DotNetUtilities.ToX509Certificate(newCert).Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "password"); 
     } 
    } 
} 

Đơn vị kiểm tra:

using System.Security.Cryptography; 
using System.Security.Cryptography.X509Certificates; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace MyApp 
{ 
    [TestClass] 
    public class CertificateGeneratorTests 
    { 
     [TestMethod] 
     public void GenerateCertificate_Test_ValidCertificate() 
     { 
      // Arrange 
      string subjectName = "test"; 

      // Act 
      byte[] actual = CertificateGenerator.GenerateCertificate(subjectName); 

      // Assert 
      var cert = new X509Certificate2(actual, "password"); 
      Assert.AreEqual("CN=" + subjectName, cert.Subject); 
      Assert.IsInstanceOfType(cert.PrivateKey, typeof(RSACryptoServiceProvider)); 
     } 
    } 
} 

Trả lời

27

Chỉ cần làm rõ, một chứng chỉ X.509 không chứa khóa riêng. Từ chứng chỉ đôi khi bị lạm dụng để thể hiện sự kết hợp của chứng chỉ và khóa riêng tư, nhưng chúng là hai thực thể riêng biệt. Toàn bộ điểm sử dụng chứng chỉ là gửi cho họ nhiều hay ít công khai, mà không gửi khóa riêng tư, mà phải được giữ bí mật. Một đối tượng X509Certificate2 có thể có khóa riêng được liên kết với nó (thông qua thuộc tính PrivateKey), nhưng đó chỉ là sự tiện lợi như là một phần của thiết kế của lớp này.

Trong ví dụ mã BouncyCastle đầu tiên của bạn, newCert thực sự chỉ là chứng chỉ và DotNetUtilities.ToX509Certificate(newCert) chỉ được tạo từ chứng chỉ.

Xem xét định dạng PKCS # 12 yêu cầu có khóa riêng, tôi khá ngạc nhiên khi phần sau thậm chí hoạt động (xem bạn đang gọi nó trên chứng chỉ không thể biết khóa riêng tư) :

.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, 
    "password"); 

(gen.Generate(kp.Private) ký giấy chứng nhận sử dụng khóa bí mật, nhưng không đặt khóa riêng trong giấy chứng nhận, trong đó sẽ không có ý nghĩa.)

Nếu bạn muốn phương pháp của bạn để trả lại cả hai chứng chỉ và khóa cá nhân bạn có thể:

  • Return một đối tượng X509Certificate2 mà bạn đã khởi tạo các tài sản PrivateKey
  • Xây dựng một PKCS # 12 cửa hàng và trả về nội dung byte[] của nó (như thể nó là một tập tin). Step 3 in the link you've sent (mirror) giải thích cách xây dựng một cửa hàng PKCS # 12.

Trả lại cấu trúc byte[] (DER) cho chính chứng chỉ X.509 sẽ không chứa khóa riêng tư.

Nếu mối quan tâm chính của bạn (theo trường hợp kiểm tra của bạn) là kiểm tra chứng chỉ được tạo từ cặp khóa RSA, bạn có thể kiểm tra loại khóa công khai của nó thay thế.

+0

+1, Rất đẹp giải thích. Bạn đã xem thông số PKCS # 12 chưa? Đó là * BRUTAL *! Tôi nghĩ mọi thứ đều có thể xảy ra với PKCS # 12; bạn không cần phải có một khóa riêng. Thông thường, PKCS # 12 được sử dụng để giữ khóa riêng tư và chứng chỉ được liên kết nhưng có các khả năng khác. –

+4

Liên kết được cung cấp cho bước 3 dường như không hoạt động. Rất may archive.org vẫn có nội dung: http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx – Kyle

+0

@Zenox, vui lòng chỉnh sửa câu trả lời của tôi và thay thế nó, điều này cũng sẽ cung cấp cho bạn một vài điểm. – Bruno

6

Tôi nhận ra đây là một bài cũ nhưng tôi tìm thấy những bài viết xuất sắc mà đi qua quá trình:

Using Bouncy Castle from .NET

+1

cho các chủ đề như vậy, meta là bạn của bạn: https://meta.stackexchange.com/questions/8231/are-answers-that-just- chứa-link-anywhere-really-good-answer – MatthewMartin

+1

Nhận cảnh báo bảo mật cho liên kết .. –

+1

Tôi đã cập nhật liên kết. – haymansfield

Các vấn đề liên quan