Search code examples
javaserializationdeserializationbouncycastlepem

How to serialize PKCS10CertificationRequest in BouncyCastle to send it over network?


I have been trying to serialize the object PKCS10CertificationRequest for a while now. I figured the right way to do it is to create an ASN1Primitive class, send it over the network, then deserialize it. However, there seems to be only serialization into ASN1, but there seems to be no deserialization from ASN1, and I don't want to manually parse and reconstruct the Request. What should I do? My code so far is

    Security.addProvider(new BouncyCastleProvider());
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC");
    kpg.initialize(1024);
    KeyPair kp = kpg.genKeyPair();
    System.out.println("Private: " + kp.getPrivate());
    System.out.println("Public: " + kp.getPublic());

    X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE);

    x500NameBld.addRDN(BCStyle.C, "AU");
    x500NameBld.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
    x500NameBld.addRDN(BCStyle.L, "Melbourne");
    x500NameBld.addRDN(BCStyle.ST, "Victoria");
    x500NameBld.addRDN(BCStyle.EmailAddress, "[email protected]");

    X500Name subject = x500NameBld.build();

    PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic());

    PKCS10CertificationRequest req1 = requestBuilder.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider("SC").build(
            kp.getPrivate()));

    JcaPKCS10CertificationRequest req2 = new JcaPKCS10CertificationRequest(req1.getEncoded()).setProvider("SC");

//serialization
    ByteArrayOutputStream abOut = new ByteArrayOutputStream();
    ASN1OutputStream berOut = new ASN1OutputStream(abOut);
    berOut.writeObject(req2.toASN1Structure());

    byte[] serializedData = abOut.toByteArray();

    ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(serializedData);
    System.out.println("");
    System.out.println("" + asn1Primitive.toString());

And the output is

[[0, [[[2.5.4.6, AU]], [[2.5.4.10, The Legion of the Bouncy Castle]], [[2.5.4.7, Melbourne]], [[2.5.4.8, Victoria]], [[1.2.840.113549.1.9.1, [email protected]]]], [[1.2.840.113549.1.1.1, NULL], #03818D0030818902818100A...

I don't want to parse this manually. What should I do instead?


Solution

  • Forget about ASN1, it is a mess, and there seems to be no automatic deserialization. However, you can use the JcaPEMWriter and PEMParser classes in BouncyCastle to create a String object to serialize or deserialize the data, and send it over the network.

        StringWriter sw = new StringWriter();
        JcaPEMWriter pemWriter = new JcaPEMWriter(sw);
        pemWriter.writeObject(req2);
        pemWriter.close();
    
        PEMParser pemParser = null;
        try
        {
           pemParser = new PEMParser(new StringReader(sw.toString()));
           Object parsedObj = pemParser.readObject();
           System.out.println("PemParser returned: " + parsedObj);
           if (parsedObj instanceof PKCS10CertificationRequest)
           {
              JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest = new JcaPKCS10CertificationRequest((PKCS10CertificationRequest)parsedObj);
              System.out.println("" + jcaPKCS10CertificationRequest.getPublicKey());
           }
        }
        catch (IOException ex)
        {
           ex.printStackTrace();
        }
        finally
        {
           if (pemParser != null)
           {
              pemParser.close();
           }
        }
    

    EDIT: Although if someone really needs to get elements out of ASN1Encodable object (like an RDN of X500Name, apparently you need the IETFUtils class as per https://stackoverflow.com/a/5527171/2413303 .