2017-06-26 29 views
0

我正在用Xamarin和BouncyCastle创建一个pfx文件。 我具有以下设置/说明书用Bouncycastle存储PKCS#12容器(pfx)

  • .NET:PCL .Net框架4.5.1
  • Xamarin:4.5.0.476
  • BouncyCastle的:BouncyCastle的签名1.7.0.1(NuGet Package

我想为我的移动客户端生成自签名证书,以对我的服务器进行身份验证。使用BouncyCastle的创作效果非常好。我的问题是,当我想将证书的私钥作为PKCS#12(pfx)容器存储并从该容器中恢复以发送我的Web请求时,会抛出一个异常,告诉我Input data cannot be coded as a valide certificate

以下是我如何创建我的证书,密钥和存储pfx容器的步骤。

创建密钥对

private AsymmetricCipherKeyPair GenerateKeyPair() 
{ 
    var random = new SecureRandom(); 
    var keyGenerationParameter = new KeyGenerationParameters(random, 4096); 
    var keyPairGenerator = new RsaKeyPairGenerator(); 
    keyPairGenerator.Init(keyGenerationParameter); 
    var keyPair = keyPairGenerator.GenerateKeyPair(); 

    return keyPair; 
} 

创建证书,这将调用创建密钥和创建容器:

public void CreateCertificate() 
{ 
    var random = new SecureRandom(); 
    var keyPair = this.GenerateKeyPair(); 

    // generate certificate generator and set public key. 
    var generator = new X509V3CertificateGenerator(); 
    generator.SetPublicKey(keyPair.Public); 

    // generate and set serial number 
    BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random); 
    generator.SetSerialNumber(serialNumber); 

    // set signing algorithm. 
    generator.SetSignatureAlgorithm(Core.Constants.SignatureAlgorithmName); 

    // set name 
    string fullQualifiedName = $"CN=dummy,O=DummyOrg,OU=Dummy"; 
    var name = new X509Name(fullQualifiedName); 
    generator.SetSubjectDN(name); 
    generator.SetIssuerDN(name); 

    // set valide time 
    generator.SetNotBefore(DateTime.UtcNow.AddHours(-1)); 
    generator.SetNotAfter(DateTime.UtcNow.AddYears(10)); 

    // add extensions. 
    var authorityKeyIdentifier = new AuthorityKeyIdentifier(
     SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public).GetEncoded(), 
     new GeneralNames(new GeneralName(name)), 
     serialNumber); 
    var subjectKeyIdentifier = new SubjectKeyIdentifier(
     SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public).GetEncoded()); 
    generator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, authorityKeyIdentifier); 
    generator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, subjectKeyIdentifier); 
    generator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(0)); 

    // generate and validate certificate. 
    var cert = generator.Generate(keyPair.Private, random); 
    cert.CheckValidity(DateTime.UtcNow); 
    cert.Verify(keyPair.Public); 

    // generate pem string 
    using (var stringWriter = new StringWriter()) 
    { 
     var writer = new PemWriter(stringWriter); 
     var pog = new PemObject("CERTIFICATE", cert.GetEncoded()); 
     writer.WriteObject(pog); 
     // pem value 
     var value = stringWriter.ToString(); 
     this.StoreCertificate(value); 
    } 

    // store the private key 
    using (var stringWriter = new StringWriter()) 
    { 
     var writer = new PemWriter(stringWriter); 
     PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private); 
     var pog = new PemObject("RSA PRIVATE KEY", info.ToAsn1Object().GetEncoded()); 
     writer.WriteObject(pog); 
     // pem value 
     var value = stringWriter.ToString(); 
     this.StorePrivateKey(value); 
    } 
    try 
    { 
     this.CreatePfxFile(); 
    } 
    catch (Exception ex) 
    { 
     throw; 
    } 
} 

创建PFX容器:

public void CreatePfxFile() 
{ 
    var certificate = this.GetCertificate(); 
    var certEntry = new X509CertificateEntry(certificate); 
    string friendlyName = certificate.SubjectDN.ToString(); 
    var privateKey = this.ReadPrivateKey(); 

    PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey); 
    byte[] keyBytes = info.ToAsn1Object().GetEncoded(); 

    var store = new Pkcs12StoreBuilder().Build(); 

    var keyEntry = new AsymmetricKeyEntry(privateKey,); 

    ////store.SetCertificateEntry(Core.Constants.CertificateAlias, certEntry); 
    store.SetKeyEntry(Core.Constants.PrivateKeyAlias, new AsymmetricKeyEntry(privateKey), new X509CertificateEntry[] { certEntry }); 

    MemoryStream stream = new MemoryStream(); 
    store.Save(stream, Core.Constants.Password.ToCharArray(), new SecureRandom()); 

    var pfxBytes = stream.ToArray(); 

    var base64 = Convert.ToBase64String(pfxBytes); 

    this.StorePfxContainer(base64); 
} 

方法GetCertificate()ReadPrivateKey()将返回X509CertificateAsymmetricKeyParameter。 方法StorePfxContainer只会将base64字符串存储到设置变量中。

注意:我删除了行store.SetCertificateEntry(Core.Constants.CertificateAlias, certEntry);,因为它导致pfx容器包含两次证书。

请不要,因为现在我们只是使用BouncyCastle的东西。

要签署我的请求我使用下面的代码。将通过从设置中检索base64字符串并使用Convert.FromBase64String(settingsValue)获取字节来创建参数cert

private void SetCertificate(HttpWebRequest request, byte[] cert) 
{ 
    try 
    { 
     System.Security.Cryptography.X509Certificates.X509Certificate2 certificate = 
     new System.Security.Cryptography.X509Certificates.X509Certificate2(cert, Core.Constants.Password); // <-- throws the exception 

     request.ClientCertificates.Add(certificate); 
    } 
    catch (Exception ex) 
    { 
     string message = ex.Message; 
    } 
} 

现在我们正在使用从.net(System.Security.Cryptography.X509Certificates.X509Certificate2)证书。这是在x509Certifcate2的构造函数时,我想加载证书引发的异常是 Input data cannot be coded as a valide certificate通过Mono.Security

我测试证书和密钥使用OpenSSL抛出,还我试图生成我的PEM一个PFX容器证书和Pem私钥也工作正常。只有当我在代码中加载证书时,它才会起作用。所以我认为容器的创建有一个我现在还没有弄清楚的错误。

感谢您的帮助。

回答

0

经过一些更多的试验和错误,我想出了我的实现错误是什么。

首先,我认为我所经历的行为是PCL和Mono的Bouncy Castle的组合。

我生成pfx容器的新方法如下所示。

private void CreatePfxFile(X509Certificate certificate, AsymmetricKeyParameter privateKey) 
{ 
    // create certificate entry 
    var certEntry = new X509CertificateEntry(certificate); 
    string friendlyName = certificate.SubjectDN.ToString(); 

    // get bytes of private key. 
    PrivateKeyInfo keyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey); 
    byte[] keyBytes = keyInfo.ToAsn1Object().GetEncoded(); 

    var builder = new Pkcs12StoreBuilder(); 
    builder.SetUseDerEncoding(true); 
    var store = builder.Build(); 

    // create store entry 
    store.SetKeyEntry(Core.Constants.PrivateKeyAlias, new AsymmetricKeyEntry(privateKey), new X509CertificateEntry[] { certEntry }); 

    byte[] pfxBytes = null; 

    var password = Guid.NewGuid().ToString("N"); 

    using (MemoryStream stream = new MemoryStream()) 
    { 
     store.Save(stream, password.ToCharArray(), new SecureRandom()); 
     pfxBytes = stream.ToArray(); 
    } 

    var result = Pkcs12Utilities.ConvertToDefiniteLength(pfxBytes); 
    this.StoreCertificate(Convert.ToBase64String(result)); 
} 

第一个变化是到MemoryStream包装成一个using。我不太确定这会改变,但它摆脱了内在的例外。

第二个变化是使用Pkcs12StoreBuilder并设置SetUseDerEcnoding(true)并从中建立商店。

第三个变化是使用Pkcs12Urilities.ConvertToDefiniteLength()方法来为数据设置一个确定的长度。

这些更改后,我的证书可以存储和恢复没有任何问题。

注意: 另外请确保您没有使用string.Empty作为密码来保存容器。我经常看到这是容器问题的答案。

相关问题