Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PublicKey handling Java / PHP

I'm currently stuck with the problem of RSA key management. To be specific, I want to create an RSA keypair in Java, sign some content (i.e. a String) export the public key, the signature, and the signed String into a JSON file (yes, a JSON!), import it on another server using PHP, and validate the signature, meaning that I have to re-create a usable key from the JSON data.

Plus, I have to do it the other way around (create in PHP, verify in Java)

Plus, I need to export the private keys into JSON (Yes, JSON again :) !) and export this.

So on the Java side, everything seems to work smoothly. I can create keys, export them to JSON, re-import them, and use them. Creating and verifying signatures is no problem. Here is the code:

creating a keypair:

public static final String  ALGORITHM           = "RSA";
public static final int     KEYSIZE             = 4096;
public static KeyPair createKeyPair() throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
    keyPairGenerator.initialize(KEYSIZE);

    return keyPairGenerator.genKeyPair();
}

exporting the keys to a String (that will end up in the JSON Object) - the code for the private Key is similar:

public static final String  PUBLICKEY_PREFIX    = "-----BEGIN PUBLIC KEY-----";
public static final String  PUBLICKEY_POSTFIX   = "-----END PUBLIC KEY-----";
public static String exportPublicKey(Key key) {
    return PUBLICKEY_PREFIX + DatatypeConverter.printBase64Binary(key.getEncoded()) + PUBLICKEY_POSTFIX;
}

The result of the above would be for example

"-----BEGIN PUBLIC KEY-----MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtBPxEtEWws2pPN5HCB795+nQyX23ZTKJt5PoMQQpwjOY/7U5ODkwHpuHWUhAuB1qTKTUdEWbe5x7WkD6/ksSib64Xq3jIeLQrfhj+g3bGsQjtca5LyIZ/J+G55l7k/Ny/lfQQNfquCcILHW7DrnzTb0D56IOBsR/r0Vv8ZvUxnaXUQtif8Q6dme8uoqzfnF46McqThnvPDxdHmhumb7tqPffzt35bRxFBvMcAWqW0FcPAeXD6cmsOBAATh/gVe1g5J89FyK8PhkNjW3uLMmknCTQg9KoWh4+DDRrLXxqSCBbaIRMCtbhShZOIbtjurJ+ZjhR/WSPnzJrl84rTjWG3Po6jsdtJ0pRHP4YnXXXJWhMt2oTOtHTQj4+99UX7Yuyp2tmFaEdQXvm3k/qbT9PBlwEovC2yqbFMcrM7sAW09NiSDdm1ipzV+vsOGuRXF2vtNX6pplifp5va5hQY/UqmlHSygvecImP5ennFOP7G62W/Q0w0qRzOXmFHN6Hsi8D1ZlWwgjyNahoX2yvgBMzy7MMYJcqiS9GOOETaenXTZViiipceGk96crjh6LG7RudMb+WN2yRXnjdWYd0GYPsaXz/faMohfXRXzRq/oIGZ4EdHhp9TknL2rCZmfR3N4Ozi1BkszAmmQeeNrUgxEjB8TdSer4p4DfR22NFcs9M3YkCAwEAAQ==-----END PUBLIC KEY-----"

then, I read the keys from the JSON again:

public static PublicKey importPublicKey(String key) throws InvalidKeySpecException, NoSuchAlgorithmException  {
    key = stripKey(key);
    byte[] keyBytes = DatatypeConverter.parseBase64Binary(key);
    return KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(keyBytes));
}

public static PrivateKey importPrivateKey(String key) throws InvalidKeySpecException, NoSuchAlgorithmException  {
    key = stripKey(key);
    byte[] keyBytes = DatatypeConverter.parseBase64Binary(key);
    return KeyFactory.getInstance(ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
}

This works fine for me. I can create, store, re-import, and use the keys - both public and private. Signatures i create using

public static String createSignature(PrivateKey privateKey, String message) {
    String algorithm = "SHA512withRSA";
    Signature signature = null;
    String signedString = null;

    try {
        signature = Signature.getInstance(algorithm);
        signature.initSign(privateKey);
        signature.update(message.getBytes());

        byte[] signatureByteArray = signature.sign();

        signedString = "-----BEGIN SIGNATURE-----"
                + DatatypeConverter.printBase64Binary(signatureByteArray)
                + "-----END SIGNATURE-----";
    } catch(Exception e) {
        e.printStackTrace();
    }
    return signedString;    
}

can be verified easily using java.security.Signature.verify(). So far I'm happy.

Now the tricky part: I send the created JSONs to the other server, and here, my trouble starts:

First, I strip the header and trailer from the string ("-----BEGIN" and so on...), then, I use base64_decode(), and ASSUME, that the result would be usable as a key with a call of openssl_pkey_get_private()

Anyhow, I get errors like

openssl_sign(): supplied key param cannot be coerced into a private key

every time I try to use my keys.

So In Java, I create a new X509EncodedKeySpec(keyBytes), yet, in PHP, there is no such functionality (?)

Where does my en/decoding go wrong? I'm actually a bit lost :(

like image 758
Xenonite Avatar asked Nov 01 '22 01:11

Xenonite


1 Answers

Your key is currently stored in binary format, which is usually known as "DER" format. That is the way Java stores the key. To be able to read it from PHP, you have to convert the key to PEM format, which is the format for OpenSSL. As a result, PHP requires that the key be in PEM format. Here is a PHP function which can convert DER encoded keys to PEM encoded keys in PHP:

function X509_to_pem($der_data) {

    $BEGIN= "-----BEGIN SIGNATURE-----";
    $END = "-----BEGIN SIGNATURE-----";

    $base64Encoded= base64_encode($der_data);

    $pem = $BEGIN . "\n";
    $pem .= chunk_split($base64Encoded, 64, "\n");
    $pem .= $END . "\n";

    return $pem;
}
like image 192
Jack Harkness Avatar answered Nov 15 '22 04:11

Jack Harkness