Generating signature using self signed certificate

2.6k Views Asked by At

I have the following sample code which I made for generating a signature using self signed certificate

public static String generateSignature(String data) throws Exception {

        System.out.println("@@inside generateSignature: " + data);

        String signature;

        String jksFilepath = "E:\\test.jks";

        try {
            // Adding Security Provider for PKCS 12
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            // Setting password for the e-Token

            // logging into token
            ks = KeyStore.getInstance("jks");


            FileInputStream fileInputStream = new FileInputStream(jksFilepath);

            // Loading Keystore
            // System.out.println("loading keystore");
            ks.load(fileInputStream, JKSPassword);
            Enumeration<String> e = ks.aliases();

            while (e.hasMoreElements()) {
                alias = e.nextElement();
                // System.out.println("Alias of the e-Token : "+ alias);

                UserCert = (X509Certificate) ks.getCertificate(alias);

                UserCertPubKey = (PublicKey) ks.getCertificate(alias).getPublicKey();

                // System.out.println("loading Private key");
                UserCertPrivKey = (PrivateKey) ks.getKey(alias, JKSPassword);
            }

            // Method Call to generate Signature
            signature = MakeSignature(data);

            return signature;

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("generateSignature" + e.getCause());
            throw new Exception();
        }

    }

    private static String MakeSignature(String data) {

        System.out.println("@@inside MakeSignature...");

        try {
            PrivateKey privateKey = (PrivateKey) ks.getKey(alias, JKSPassword);
            myPubCert = (X509Certificate) ks.getCertificate(alias);
            Store certs = new JcaCertStore(Arrays.asList(myPubCert));

            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();

            generator.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").build("SHA256withRSA", privateKey, myPubCert));

            generator.addCertificates(certs);

            CMSTypedData data1 = new CMSProcessableByteArray(data.getBytes());

            CMSSignedData signed = generator.generate(data1, true);

            BASE64Encoder encoder = new BASE64Encoder();

            String signedContent = encoder.encode((byte[]) signed.getSignedContent().getContent());

            String envelopedData = encoder.encode(signed.getEncoded());

            return envelopedData;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("MakeSignature ==" + e.getCause());
            return "";
        }
    }

There are some associated functions as well, but for the sake of giving brief I am not adding it.

Now I want to do the exact same things using PHP.

JKS doesnt work on PHP as its keystore for Java.

I tried open_ssl functions with different sets of encryption methods. But I am not getting the expected result which is as same as what I get via this java code ("not same" is about the bit rate and length of generated signature).

Can someone help me to implement the same signature generation in PHP, please?

2

There are 2 best solutions below

1
On BEST ANSWER

I think the PHP official document is very clear: http://php.net/manual/en/function.openssl-csr-new.php

Example #1 Creating a self-signed certificate

<?php
$dn = array(
    "countryName" => "GB",
    "stateOrProvinceName" => "Somerset",
    "localityName" => "Glastonbury",
    "organizationName" => "The Brain Room Limited",
    "organizationalUnitName" => "PHP Documentation Team",
    "commonName" => "Wez Furlong",
    "emailAddress" => "[email protected]"
);

// Generate a new private (and public) key pair
$privkey = openssl_pkey_new(array(
    "private_key_bits" => 2048,
    "private_key_type" => OPENSSL_KEYTYPE_RSA,
));

// Generate a certificate signing request
$csr = openssl_csr_new($dn, $privkey, array('digest_alg' => 'sha256'));

// Generate a self-signed cert, valid for 365 days
$x509 = openssl_csr_sign($csr, null, $privkey, $days=365, array('digest_alg' => 'sha256'));

// Save your private key, CSR and self-signed cert for later use
openssl_csr_export($csr, $csrout) and var_dump($csrout);
openssl_x509_export($x509, $certout) and var_dump($certout);
openssl_pkey_export($privkey, $pkeyout, "mypassword") and var_dump($pkeyout);

// Show any errors that occurred here
while (($e = openssl_error_string()) !== false) {
    echo $e . "\n";
}

Then you can call openssl_sign: http://php.net/manual/en/function.openssl-sign.php , use the generated private key to sign.

If you want to use the Java(JKS)'s key in PHP code, you should export the keys first, and then use PHP function load the keys.

0
On

The following code Java and PHP takes a private key from a PKCS12 keystore (keystore.pfx) and signs the content of the data.txt file. Using the same keystore and data both implementations return exactly the same output:

I used plain Java only (no bouncycastle) as the java.security classes can handle PKCS12 input very well:

public static void main(String[] args) throws Exception {

    String keyStoreFile = "keystore.pfx";
    char[] password = "password".toCharArray();

    String dataFile = "data.txt";

    PrivateKey priv = loadPrivateKey(keyStoreFile, password);

    byte[] signature = signData(priv, dataFile);

    System.out.println(Base64.getEncoder().encodeToString(signature));
}

private static byte[] signData(PrivateKey priv, String dataFile) throws Exception {
    Signature dsa = Signature.getInstance("SHA256withRSA");

    dsa.initSign(priv);

    try (FileInputStream fis = new FileInputStream(dataFile);
         BufferedInputStream bufin = new BufferedInputStream(fis);) {

        byte[] buffer = new byte[1024];
        int len;
        while ((len = bufin.read(buffer)) >= 0) {
            dsa.update(buffer, 0, len);
        }

        bufin.close();

        byte[] realSig = dsa.sign();
        return realSig;
    }
}

private static PrivateKey loadPrivateKey(String keyStoreFile, char[] password) throws Exception {
    try (FileInputStream fin = new FileInputStream(keyStoreFile)) {
        KeyStore ks = KeyStore.getInstance("PKCS12", "SunJSSE");
        ks.load(fin, password);
        PrivateKey priv = (PrivateKey) ks.getKey("1", password);
        return priv;
    }
}

And the PHP version:

<?php
//data you want to sign
$data = file_get_contents("data.txt");

$cert_store = file_get_contents("keystore.pfx");
openssl_pkcs12_read($cert_store, $cert_info, "password");

//create signature
openssl_sign($data, $signature, $cert_info['pkey'], OPENSSL_ALGO_SHA256);

//finally encode
$r = base64_encode($signature);
print $r;
?>

I used OpenSSL to generate the PKCS12 keystore.pfx file:

# generate new RSA private key
openssl genrsa -out private.pem 1024
# CSR and signed certificate are needed to export as PKCS12 store
openssl req -new -key private.pem -out certificate.csr
openssl x509 -req -days 365 -in certificate.csr -signkey private.pem -out certificate.crt
# export as PKCS12 keystore
openssl pkcs12 -export -out keystore.pfx -inkey private.pem -in certificate.crt -passout pass:password

You can sign the data.txt using OpenSSL as well:

openssl dgst -sha256 -sign private.pem < data.txt | openssl base64

All version will output the same result.

If you have a JKS keystore and want to use the private key stored in this keystore, you can export the JKS keystore to PKCS12:

keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.pfx \
    -srcstoretype JKS -deststoretype PKCS12 -deststorepass password \
    -srcalias alias -destalias 1

One more thing to note as this always seems to be confused:

You don't sign data using a certificate. You sign data using a (private) key. A certificate is more or less simply a piece of data signed with a private key. A self-signed certificate is signed with your own private key. While a certificate issued by a certificate authority (CA) is signed with the CA's private key.

In the above example the generated certificate signing request (CSR) and certificate are basically only created to import the private key into the PKCS12 keystore. You could use the plain private.pem key file as well for the signing purpose, but as you were using a PKCS12 keystore I did the same.