Search code examples
c#bouncycastlex509

Use a PFX File X509 for Encrypt/Decrypt


I have a pfx file that is for test purposes and have no special security.

I need to use this file to encrypt a XML file. Scenario is that I need to send a Password-Zip file to a client along with a Metadata file relating to the Zip. The Zip will actually contain file for updating a software installation. At first, i though I'd encrypt the Zip itself. But then, it was decided that it'll be protected with a simple password and will be uploaded to a web site (ASP.NET) at client's. The client will also upload a metadata file that will be given with the Zipped update. This Metadata file will have stuff like Client identifier, MAC Address, MD5 Hash of update zip file etc... I need to encrypt this metadata file before sending and decrypt once the client uploads both ZIP and Meta. Once decrypted, I'll try to verify client and the current installation and the patch itself.

The PFX is generated for me.

  • What will I need to extract from the PFX
  • I can use BouncyCastle here.
  • The idea was that once the client install the software at his end, he'll generate a X509 certificate request. That will create two thing: the request and a private key. The client will keep the private key and send us the request. We'll validate the client (payment and all) and generate a certificate for him and email him back (the PFX). He'll then import it to unlock the features on the software and be able to get zipped updates and further stuff

The question is a bit of a complete solution demand like but I'm facing a real hard time then it comes to signing with X509. I'm looking at BouncyCastle docs right now.

Thanks for the read. Any help will be appreciated.


Solution

  • sry bout the long answer.

    Yes you can use bouncycastle to extract a certificate from a pfx.

    var pkcs = new Pkcs12Store(File.Open("path.pfx", FileMode.Open), "password".ToCharArray());
    pkcs.Aliases // is a list of certificate names that are in the pfx;
    pkcs.GetCertificate(alias); // gets a certificate from the pfx
    

    We use a very similar approach but we send the certificate without the private key back in PEM, since it doesn't contain any "secret's"

    This is how you make a certificate signing request:

    //generate a privatekey and public key
    RsaKeyPairGenerator rkpg1 = new RsaKeyPairGenerator();
    rkpg1.Init(new KeyGenerationParameters(new SecureRandom(), Keystrength));
    AsymmetricCipherKeyPair ackp1 = rkpg1.GenerateKeyPair();
    
    RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)ackp1.Private;
    RsaKeyParameters publicKey = (RsaKeyParameters)ackp1.Public;
    
    X509Name comonname = new X509Name (cname);
    Pkcs10CertificationRequest csr = new Pkcs10CertificationRequest ("SHA1WITHRSA", comonname, publicKey, null, privateKey);
    csr.Verify ();
    
    StringBuilder sb = new StringBuilder();
    PemWriter pw = new PemWriter (new StringWriter (sb));
    pw.WriteObject (csr);
    pw.Writer.Flush ();
    var pemstring = sb.ToString ();
    

    This is what happens at the server end signing the certificate signing request:

    As far as I understand the root certificate can be any certificate of which you have a private key. And if you want to use it in other application like a browser it needs to be inserted in to the certificate store called "Trusted Root Certificate Authorities", if it is a self signed certificate, or "Trusted Intermediate Certificate Authorities", if it isn't self signed at the client computer without the pirvate key off course.

    A certificate with a private key is identifiable by the little key added to the icon see here; self signed certificate with comonname localhost

    //rootCert contains the rootcertificate in bouncycastle format
    
    var pemstring = "a string containing the PEM";
    PemReader pr = new PemReader (new StringReader (pemstring));
    Pkcs10CertificationRequest csr = (Pkcs10CertificationRequest)pr.ReadObject ();
    
    X509V3CertificateGenerator certgen = new X509V3CertificateGenerator();
    certgen.SetSubjectDN(csr.GetCertificationRequestInfo().Subject);
    certgen.SetIssuerDN(rootCert.SubjectDN);
    certgen.SetPublicKey(csr.GetPublicKey());
    certgen.SetSignatureAlgorithm(csr.SignatureAlgorithm.ObjectID.Id);
    certgen.SetNotAfter(validThrough); //a datetime object
    certgen.SetNotBefore(validFrom); //a datetime object
    certgen.SetSerialNumber(serialNumber); //a biginteger
    
    X509Certificate clientcert = certgen.Generate(rootPrivateKey);
    
    //to send the certificate without the private key to the client you'll have to 
    //convert it to PEM:
    
    StringBuilder sb = new StringBuilder();
    PemWriter pw = new PemWriter (new StringWriter (sb));
    pw.WriteObject (clientcert);
    pw.Writer.Flush ();
    var pemstring = sb.ToString ();
    
    //to make it a .net certificate use:
    X509Certificate2 netcert = DotNetUtilities.ToX509Certificate (clientcert);
    

    The client certificatie does NOT contain a private key atm. Thats what happens at the client end again like so:

    //where pemstring contains the certificate in a PEMstring like shown above.
    //and privateKey is the one we had in the first part over at the client.
    
    PemReader pr = new PemReader(new StringReader(pemstring));
    X509Certificate2 cert = DotNetUtilities.ToX509Certificate((Bouncy.X509Certificate)pr.ReadObject());
    
    CspParameters cparms = new CspParameters
    {
        CryptoKeySecurity = new CryptoKeySecurity(),
        Flags = CspProviderFlags.UseMachineKeyStore
    };
    
    RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(cparms);
    RSAParameters parms = new RSAParameters
    {
        Modulus = privateKey.Modulus.ToByteArrayUnsigned (),
        P = privateKey.P.ToByteArrayUnsigned (),
        Q = privateKey.Q.ToByteArrayUnsigned (),
        DP = privateKey.DP.ToByteArrayUnsigned (),
        DQ = privateKey.DQ.ToByteArrayUnsigned (),
        InverseQ = privateKey.QInv.ToByteArrayUnsigned (),
        D = privateKey.Exponent.ToByteArrayUnsigned (),
        Exponent = privateKey.PublicExponent.ToByteArrayUnsigned ()
    };
    
    rcsp.ImportParameters(parms);
    
    netcert.PrivateKey = rcsp;