Подписание объектов JSON

Я должен обмениваться объектами JSON между различными платформами и реализациями сервиса и сделать его целостность проверяемой с помощью цифровых подписей. Таким образом, платформа A создаст такой объект и создаст цифровую подпись. Затем указанная подпись включается в объект и отправляется на платформу B. объекты JSON могут содержать произвольные атрибуты и данные.

Е. Г. в PHP:

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

    openssl_sign($msgToSign, $jsonObjectToSign->signature, $privateKey, OPENSSL_SLGO_SHA1);

    return $jsonObjectToSign;
}

проблема в том, что, например, в Java, нет способа узнать, атрибуты объекта JSON будут в том же порядке, в котором вы их добавили (через JSONObject.ставить.))( Итак, если я сделаю

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

в PHP подпишите этот объект, как указано выше, перенесите его на сервер на базе java, декодируйте объект json, а затем попробуйте проверить подпись, Я, вероятно, получу другой порядок атрибутов объекта.

Итак, мне нужен надежный способ создания строки из JSONObject, независимо от используемого языка или платформы.

в пример объекта выше должен всегда выводить {"a":1, "b":2} и не {"b":2, "a":1}. К сожалению, это обычный случай, например, в Java.

есть ли какая-либо" лучшая практика " для подписания объектов JSON безопасным способом?

но позвольте мне описать проблему по-другому:

предположим, я хочу сделать это на Java (или любом другом языке):

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

теперь мне нужна функция сериализации, которая выводит всегда одно и то же строковое представление для этого объекта, независимо от того, как и на каком языке создается этот объект.

5 ответов


подписание и шифрование объектов JSON указано в наборе спецификаций JOSE, где JOSE означает подписание и шифрование объектов Javascript, см. http://jose.readthedocs.org/en/latest/ JOSE использует отсоединенную подпись, вычисленную над представлением кодирования base64url объекта JSON. Подпись не является частью самого объекта JSON, поэтому для ее проверки не требуется повторное упорядочение.


поскольку нет официального (ни неофициального) стандарта AFAIK на подписание JSON, я бы, вероятно, сделал пользовательскую реализацию. Я бы определил новый объект JSON, например

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

и реализовать уровень проверки подписи / подписи на обеих / всех сторонах моей системы.


вот как я решил это сейчас. Это как-то похоже на то, что делает Хосе, за исключением заголовка. Но Хосе, кажется, приносит много накладных расходов (и функций), которые мне не нужны. Поэтому я решил пойти со следующим:

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);
    }
}

JsonObject js = new JsonObject();
js.addProperty("c", "123");
js.addProperty("t", "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
System.out.println("Json object == " + js);
GenerateSignature util = new GenerateSignature();
String ecodedToken = util.signJSONObject(js);

мы столкнулись с аналогичной проблемой с хэшированием полезных нагрузок в кодировке JSON. В нашем случае мы используем следующую методику:

  1. преобразование данных в объект JSON
  2. кодировать полезную нагрузку JSON в base64
  3. дайджест сообщений (HMAC) сгенерированная полезная нагрузка base64
  4. передать полезную нагрузку base64 (с сгенерированной сигнатурой) .

подробности смотрите по ссылке ниже

связанный ответ здесь : как криптографической хэш-объект JSON?