签署 JSON 个对象

Signing JSON objects

我必须在不同平台和服务实现之间交换 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.put())。所以,如果我做一个

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

in PHP,如上所述签署这个对象,将其传输到基于 java 的服务器,解码 json 对象然后尝试验证签名,我会可能得到对象属性的不同顺序。

所以我需要的是一种从 JSON对象创建字符串的可靠方法,与使用的语言或平台无关。

上面的示例对象需要始终输出 {"a":1, "b":2} 而从不输出 {"b":2, "a":1}。不幸的是,这是常见的情况,例如在 Java.

有没有"best practice"以安全的方式签署JSON对象?

但让我用另一种方式来描述这个问题:

假设我想用 Java(或任何其他语言)执行此操作:

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

现在,我需要一个序列化函数,它始终为该对象输出相同的字符串表示形式,无论该对象是如何创建的以及使用何种语言创建的。

由于 AFAIK 还没有关于 JSON 签名的官方(既非官方)标准,我可能会做一个自定义实现。我会定义一个新的 JSON 对象,例如

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

并在我系统的 both/all 端实施签名/签名验证层。

JSON 对象的签名和加密在 JOSE 规范套件中指定,其中 JOSE 代表 Javascript 对象签名和加密,请参阅 http://jose.readthedocs.org/en/latest/ JOSE 使用独立签名计算JSON 对象的 base64url 编码表示。签名不是 JSON 对象本身的一部分,因此不需要重新排序来验证它。

我现在就是这样解决的。除了 header 之外,它与 JOSE 所做的有点相似。但是 JOSE 似乎带来了很多我不需要的开销(和功能)。所以我决定采用以下方法:

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-encoded 有效载荷时遇到了类似的问题。在我们的案例中,我们使用以下方法:

  1. 将数据转换成JSON对象
  2. 在 base64 中编码 JSON 有效负载
  3. 消息摘要 (HMAC) 生成的 base64 负载
  4. 传输 base64 负载(带有生成的签名)。

详情见下方link

link在这里回答: How to cryptographically hash a JSON object?