2017-06-26 5 views
0

私はXamarinとBouncyCastleでpfxファイルを作成するのに苦労しています。 私は、次の設定/仕様BouncycastleでPKCS#12 Container(pfx)を保存

  • .NET持っている:PCLの.NET Framework 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コンテナを保存する手順を示します。

鍵のペアを作成します。

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); 
} 

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コンテナを作成します。

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; 
} 

は、キーを作成するために呼び出すと、コンテナを作成する証明書を作成します

GetCertificate()およびReadPrivateKey()のメソッドは、X509CertificateおよびAsymmetricKeyParameterである。 メソッドStorePfxContainerは、base64文字列を設定変数に格納するだけです。

注:私は、store.SetCertificateEntry(Core.Constants.CertificateAlias, certEntry);という行を削除しました。これは、pfxコンテナが証明書を2回含むためです。

私たちはBouncyCastleのものを使用しているので、今は使用しないでください。

私の要求に署名するには、次のコードを使用します。パラメータcertは、設定からbase64文字列を取得し、Convert.FromBase64String(settingsValue)を使用してバイトを取得することによって作成されます。今、私たちは、ネット(System.Security.Cryptography.X509Certificates.X509Certificate2)からの証明書を使用している

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; 
    } 
} 

。私は、証明書をロードする際 x509Certifcate2のコンストラクタでスローされる例外はMono.Security

によってスロー Input data cannot be coded as a valide certificateである私も、私は私のPEMからPFXコンテナを生成しようとしました、OpenSSLので証明書と鍵をテスト証明書とpem秘密鍵も正常に動作します。私のコードの中で証明書を読み込むときだけ、それは動作しません。だから私は、コンテナの作成は私が今考え出していないバグがあると思う。

ありがとうございました。

答えて

0

もう一度試行錯誤した後、実装のエラーが何か分かりました。

まず、私が経験したふるまいは、Bouncy Castle for PCLとMonoの組み合わせだったと思います。

私の新しい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)); 
} 

最初の変更はusingMemoryStreamをラップすることでした。私はこれが何が変わるのかはわかりませんが、内部の例外を取り除いています。

Pkcs12StoreBuilderを使用してSetUseDerEcnoding(true)を設定し、これでストアを構築しました。

第3の変更点は、Pkcs12Urilities.ConvertToDefiniteLength()メソッドを使用してデータに一定の長さを設定することでした。

これらの変更後、私の証明書は問題なく保存および復元できます。

注: また、あなたは、コンテナを保存するパスワードとしてstring.Emptyを使用していないことを確認してください。私はこれをしばしばコンテナの問題に対する答えと見てきました。

関連する問題