2015-08-03 12 views
6

Devo scambiare oggetti JSON tra diverse piattaforme e implementazioni di un servizio e rendere la sua integrità verificabile tramite firme digitali. Quindi una piattaforma A creerebbe un tale oggetto e creerebbe una firma digitale. Tale firma viene quindi inclusa nell'oggetto e inviata alla piattaforma B. Gli oggetti JSON possono contenere attributi e dati arbitrari.Firma oggetti JSON

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

Il problema è che ad es. in Java, non è possibile stabilire se gli attributi di un oggetto JSON saranno nello stesso ordine in cui sono stati aggiunti (tramite JSONObject.put()). Quindi, se faccio un

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

in PHP, firmare questo oggetto come detto in precedenza, il trasferimento a un server basato su Java, decodificare l'oggetto JSON e quindi provare a verificare la firma, probabilmente avrei avuto un diverso ordine degli attributi dell'oggetto.

Quindi, quello che mi serve, è un modo affidabile per creare una stringa da un oggetto JSONObject, indipendentemente dalla lingua o dalla piattaforma utilizzata.

L'esempio precedente deve sempre emettere {"a":1, "b":2} e MAI {"b":2, "a":1}. Sfortunatamente, questo è il solito caso, ad es. in Java.

Esiste una "best practice" per firmare oggetti JSON in modo sicuro?

Ma permettetemi di descrivere il problema in un altro modo:

Diciamo che voglio fare questo in Java (o qualsiasi altra lingua):

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

Ora, ho bisogno di una funzione di serializzazione, che restituisce sempre la stessa rappresentazione di stringa per questo oggetto, indipendentemente da come e con quale linguaggio viene creato questo oggetto.

+0

Non sono sicuro se [questo] (http://stackoverflow.com/questions/4670494/how-to-cryptographically-hash-a-json-object) è un duplicato, ma vale sicuramente la pena leggerlo. Alcune idee sono proposte per canonicalizzare JSON per garantire un hash riproducibile. –

risposta

0

Ecco come ho risolto ora. È in qualche modo simile a ciò che fa JOSE, ad eccezione dell'intestazione. Ma JOSE sembra portare un sacco di spese generali (e funzionalità) di cui non ho bisogno. Così ho deciso di andare con il seguente:

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

come hai verificato l'ordine degli elementi di JSON in altre piattaforme e lingue? –

+0

Il 'jsonToSign' è serializzato come una stringa e quindi firmato. Ciò consente a qualsiasi implementazione JSON di verificare la firma, ma richiede un ulteriore passaggio di codifica/decodifica per ottenere i dati effettivi. –

2

Dato che AFAIK non ha ancora uno standard ufficiale (non ufficiale) su JSON Signing, probabilmente eseguirò un'implementazione personalizzata. Definirei un nuovo oggetto JSON, ad es.

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

e implementare uno strato di verifica firma/firma su entrambi i lati del sistema.

+0

Questo è un po 'quello che sto facendo in questo momento.Comunque, quando uso l'oggetto e poi creo la rappresentazione base64, probabilmente otterrò una rappresentazione base64 diversa su sistemi diversi. Quindi, il controllo della firma probabilmente fallirà. – Xenonite

+1

No, Base64 dovrebbe essere lo stesso (IMHO). O intendi, ad es. l'ordine dei campi nella stringa JSON originale può essere diverso e quindi la stringa Base64 sarà diversa? Ma questo dovrebbe essere un problema, firmerai la stringa Base64, non quella originale. E non devi preoccuparti per l'ordine dei campi, poiché sul sistema di destinazione devi prima convalidare la firma ** della stringa Base64 **, quindi decodificarla per ottenere l'originale e infine caricarla. Ma decodificando e caricando non ti interessa più la firma. –

+0

Sì, questo è il problema di cui sto parlando. Sfortunatamente, non riesco a "ottenere" questa rappresentazione String nel mio Framework, ma ottengo solo l'oggetto JSON. Quindi ho bisogno di un modo per creare sempre la stessa rappresentazione base64. – Xenonite

3

La firma e la crittografia degli oggetti JSON sono specificate nella suite di specifiche JOSE dove JOSE sta per JavaScript Signing e Encryption di Javascript, vedere http://jose.readthedocs.org/en/latest/ JOSE utilizza una firma separata calcolata su una rappresentazione di codifica base64url dell'oggetto JSON. La firma non fa parte dell'oggetto JSON stesso, quindi non è necessario riordinare per convalidarlo.

+0

JOSE non è ancora definitivo, vero? –

+0

sono, vedi: http://self-issued.info/?p=1387 –

+0

Grazie, non l'ho notato. –

-1
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); 
+0

Benvenuti nello stack overflow :-) Per favore guarda [risposta]. Dovresti fornire alcune informazioni sul perché il tuo codice risolve il problema. Le risposte al solo codice non sono utili per la comunità. – JimHawkins

Problemi correlati