签署 JSON 对象
Posted
技术标签:
【中文标题】签署 JSON 对象【英文标题】:Signing JSON objects 【发布时间】:2015-10-25 18:24:58 【问题描述】:我必须在不同平台和服务实现之间交换 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');
在 PHP 中,如上所述对该对象进行签名,将其传输到基于 java 的服务器,解码 json 对象,然后尝试验证签名,我可能会得到对象属性的不同顺序。
所以我需要的是一种从 JSONObject 创建字符串的可靠方法,与所使用的语言或平台无关。
上面的示例对象需要始终输出"a":1, "b":2
和NEVER "b":2, "a":1
。不幸的是,这是通常的情况,例如在 Java 中。
是否有任何“最佳实践”以安全的方式?
但是让我用另一种方式来描述这个问题:
假设我想用 Java(或任何其他语言)执行此操作:
JSONObject j = new JSONObject();
j.put("a", 1);
j.put("b", 2);
现在,我需要一个序列化函数,它始终为该对象输出相同的字符串表示形式,无论该对象是如何以及使用何种语言创建的。
【问题讨论】:
不确定this 是否重复,但绝对值得一读。提出了一些用于规范化 JSON 以保证可重现哈希的想法。 我在问题中看到了OPENSSL_SLGO_SHA1
,但不应该是OPENSSL_ALGO_SHA1
带有A 而不是S?跨度>
【参考方案1】:
JSON 对象的签名和加密在 JOSE 规范套件中指定,其中 JOSE 代表 javascript 对象签名和加密,请参阅 http://jose.readthedocs.org/en/latest/ JOSE 使用基于 JSON 对象的 base64url 编码表示计算的分离签名。签名不是 JSON 对象本身的一部分,因此无需重新排序即可对其进行验证。
【讨论】:
【参考方案2】:由于 AFAIK 还没有关于 JSON 签名的官方(也不是非官方)标准,我可能会做一个自定义实现。我会定义一个新的 JSON 对象,例如
"original": "..." // original JSON as a Base64 encoded string
"signature": "..." // the signature
并在我的系统的两侧/所有方面实施签名/签名验证层。
【讨论】:
这就是我现在正在做的事情。无论如何,当使用对象然后创建所说的 base64 表示时,我可能会在不同的系统上得到不同的 base64 表示。因此,检查签名可能会失败。 不,Base64 应该相同(恕我直言)。或者你的意思是,例如原始 JSON 字符串中的字段顺序可能不同,因此 Base64 字符串会有所不同?但这应该没问题,您将签署 Base64 字符串,而不是原始字符串。而且您不必关心字段顺序,因为在目标系统上您首先验证 Base64 字符串的签名,然后对其进行解码以获取原始文件,最后加载它。但是通过解码和加载,您不再关心签名了。 是的,这就是我要说的问题。不幸的是,我没有在我的框架中“得到”这个字符串表示,我只是得到了 JSON 对象。所以我需要一种方法来创建始终相同的 base64 表示。【参考方案3】:这就是我现在解决的方法。它在某种程度上类似于 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);
【讨论】:
您是如何验证 JSON 在其他平台和语言中的元素顺序的? “jsonToSign”被序列化为字符串,然后签名。这允许任何 JSON 实现来验证签名,但需要额外的编码/解码步骤来获取实际数据。 等一下,@Xenonite。if (gettype(...) == 'string') throw new Exception("not a string");
这似乎与预期相反。【参考方案4】:
我们在散列 JSON 编码的负载时遇到了类似的问题。在我们的例子中,我们使用以下方法:
-
将数据转换为 JSON 对象
用 base64 编码 JSON 负载
消息摘要 (HMAC) 生成的 base64 有效负载
传输 base64 有效负载(带有生成的签名)。
查看以下链接中的详细信息
链接的答案在这里: How to cryptographically hash a JSON object?
【讨论】:
【参考方案5】: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);
【讨论】:
欢迎来到堆栈溢出 :-) 请看How to Answer。您应该提供一些信息为什么您的代码可以解决问题。纯代码答案对社区没有用处。以上是关于签署 JSON 对象的主要内容,如果未能解决你的问题,请参考以下文章
如何使用返回的 json 对象中的特定项目设置反应组件的状态?
在签署 jwt 时,只保留 user_id 或整个用户对象是不是更好?
gpg 未能签署数据致命:未能写入提交对象 [Git 2.10.0]