Signing JSON objects

13,090

Solution 1

This is how I solved it now. It is somehow similar to what JOSE does, except for the header. But JOSE seems to bring a lot of overhead (and features) I dont need. So I decided to go with the following:

class Signature
{
    private static $algorithm = OPENSSL_ALGO_SHA512;
    private static $signaturePrefix = '-----BEGIN SIGNATURE-----';
    private static $signaturePostfix = '-----END SIGNATURE-----';

    public static function createSignature($message, $privateKey)
    {
        $signature = null;

        openssl_sign($message, $signature, $privateKey, self::$algorithm);

        return self::$signaturePrefix . base64_encode($signature) . self::$signaturePostfix;
    }

    public static function verifySignature($message, $publicKey, $signature)
    {
        $signature = str_replace(self::$signaturePrefix, '', $signature);
        $signature = str_replace(self::$signaturePostfix, '', $signature);

        return openssl_verify($message, base64_decode($signature), $publicKey, self::$algorithm);
    }

    public static function signJSON($jsonToSign, $privateKey)
    {
        if(gettype($jsonToSign) != 'string')
            $jsonToSign = json_encode($jsonToSign);

        $signedJSON = json_decode('{}');
        $sigedJSON->signature = self::createSignature($message, $privateKey);
        $signedJSON->object = $jsonToSign;

        return $signedJSON;
    }

    public static function verifyJSONSignature($jsonObject, $publicKey)
    {
        if(gettype($jsonObject->object) == 'string')
            throw new Exception('Value $jsonObject->object must be a String, is a ' . gettype($jsonObject->object));

        return self::verifySignature($jsonObject->object, $publicKey, $jsonObject->signature);
    }
}

Solution 2

Signing and encryption of JSON objects is specified in the JOSE suite of specifications where JOSE stands for Javascript Object Signing and Encryption, see http://jose.readthedocs.org/en/latest/ JOSE uses a detached signature calculated over a base64url encode representation of the JSON object. The signature is not part of the JSON object itself so no re-ordering is required to validate it.

Solution 3

As there is AFAIK no official (neither unofficial) standard on JSON Signing yet, I'd probably do a custom implementation. I'd define a new JSON object, e.g.

{
  "original": "..." // original JSON as a Base64 encoded string
  "signature": "..." // the signature
}

and implement a signing / signature verification layer on both/all sides of my system.

Solution 4

We encountered a similar issue with hashing JSON-encoded payloads. In our case we use the following methodology:

  1. Convert data into JSON object
  2. Encode JSON payload in base64
  3. Message digest (HMAC) the generated base64 payload
  4. transmit base64 payload (with the generated signature) .

See the details in the link below

linked answer here : How to cryptographically hash a JSON object?

Share:
13,090

Related videos on Youtube

Xenonite
Author by

Xenonite

I posted this here just to get the badge ;) Seems like this needs some more lines... Still not enough? How much do I need to write?

Updated on September 29, 2022

Comments

  • Xenonite
    Xenonite over 1 year

    I have to exchange JSON objects between different platforms and implementations of a service and make its integrity verifiable via digital signatures. So a platform A would create such an object and create a digital signature. Said signature is then included into the object and sent to platform B. The JSON objects can contain arbitrary attributes and data.

    E.g. in PHP:

    function signObject($jsonObjectToSign, $privateKey) {
        $jsonObjectToSign->signature = "";
        $msgToSign = json_encode($jsonObjectToSign);
    
        openssl_sign($msgToSign, $jsonObjectToSign->signature, $privateKey, OPENSSL_SLGO_SHA1);
    
        return $jsonObjectToSign;
    }
    

    Problem is, that e.g. in Java, there is no way to tell whether the attributes of a JSON object will be in the same order you added them (via JSONObject.put()). So, if I do a

    $json = json_encode('{"a":1, "b":2}');
    

    in PHP, sign this object as stated above, transfer it to a java based server, decode the json object and then try to verify the signature, I'd probably get a different order of the object's attributes.

    So what I need, is a reliable way to create a String from a JSONObject, independent of the language or platform used.

    The example object above needs always to output {"a":1, "b":2} and NEVER {"b":2, "a":1}. Unfortunately, this is the usual case e.g. in Java.

    Is there any "best practice" to sign JSON Objects in a secure way?

    But let me describe the problem in another way:

    Let's say I want to do this in Java (or any other language):

    JSONObject j = new JSONObject();
    j.put("a", 1);
    j.put("b", 2);
    

    Now, I need a serialization function, that outputs always the same string representation for this object, no matter how and with what language this object is created.

  • Xenonite
    Xenonite over 8 years
    This is kinda what I'm doing right now. Anyhow, when using the Object and then creating said base64 representation, I'll probably get a different base64 representation on different systems. Hence, checking the signature will probably fail.
  • Jozef Chocholacek
    Jozef Chocholacek over 8 years
    No, Base64 should be the same (IMHO). Or you mean, that e.g. the order of fields in the original JSON string can differ, and thus the Base64 string will differ? But this should be no problem, you will sign the Base64 string, not the original one. And you don't have to care for the field order, as on the target system you first validate the signature of the Base64 string, then decode it to get the original, and finally load it. But by decoding and loading you don't care for the signature anymore.
  • Xenonite
    Xenonite over 8 years
    Yes, that is the problem I'm talking about. Unfortunately, I don't "get" this String representation in my Framework, I just get the JSON Object. So I need a way to create always the same base64 representation.
  • Hossein Nasr
    Hossein Nasr over 7 years
    how did you verify the order of elements of JSON in other platform and languages?
  • Rich Remer
    Rich Remer about 7 years
    The 'jsonToSign' is serialized as a string and then signed. This allows any JSON implementation to verify the signature, but requires an extra encoding/decoding step to get at the actual data.
  • JimHawkins
    JimHawkins almost 7 years
    Welcome to stack overflow :-) Please look at How to Answer. You should provide some information why your code solves the problem. Code-only answers aren't useful for the community.
  • mickmackusa
    mickmackusa over 2 years
    Wait a sec, @Xenonite. if (gettype(...) == 'string') { throw new Exception("not a string"); } that seems the opposite of what is intended.