V návaznosti na předchozí článek, ve kterém jsme XML podpis vytvářeli, budeme pokračovat ověřením platnosti všech XML podpisů obsažených v XML dokumentu.

Nalezení elementů podpisu

Nejprve potřebujeme v dokumentu najít XML podpisy, které budeme ověřovat. V našem případě víme, že se element ds:Signature nachází v dokumentu jako potomek kořenového elementu dokument. Pokud bychom však dostali k ověření neznámý XML soubor obsahující nějaké podpisy, mohli bychom najít všechny elementy podpisů například vyhledáváním pomocí XPath. Příslušný XPath výraz, který identifikuje všechny elementy ds:Signature obsažené v podstromu aktuálního uzlu (počítaje tento aktuální uzel do podstromu), vypadá takto: descendant-or-self::ds:Signature.

Abychom se nemuseli vázat na používání konkrétního prefixu ds pro jmenný prostor XML podpisu v ověřovaných souborech, implementujeme rozhraní NamespaceContext, které bude obsahovat mapování prefixu ds na URI http://www.w3.org/2000/09/xmldsig# a naopak. Tuto naši implementaci rozhraní NamespaceContext pak předhodíme instanci třídy XPath, aby věděla, jakému jmennému prostoru odpovídá prefix ds použitý v našem XPath výrazu. Odpovídající kód je celkem krátký:

// Vytvoření instance třídy XPath
final XPath xpath = XPathFactory.newInstance().newXPath();
// Nastaveni mapování jmenných prostorů a jejich prefixů
xpath.setNamespaceContext(MyNamespaceContext.getInstance());
// Nalezení výsledné množiny uzlů
final NodeList signatureElements = (NodeList) xpath.evaluate(
   „descendant-or-self::ds:Signature“, ancestor,
   XPathConstants.NODESET);

Ověření platnosti podpisu

Když jsme všechny podpisy obsažené v dokumentu našli, můžeme přikročit k ověření jejich platnosti. Zjištění, zda je podpis jako celek platný, je také docela jednoduché:

// Získání poskytovatele XML podpisu
final XMLSignatureFactory fac = XMLSignatureFactory.getInstance(„DOM“);
for (int i = 0; i < signatureElements.getLength(); i++) {
   // Vytvoření kontextu pro validaci
   final DOMValidateContext valContext = new DOMValidateContext(
      new MyKeySelector(), signatureElements.item(i));
   // Získání XML podpisu z validačního kontextu
   final XMLSignature signature = fac
      .unmarshalXMLSignature(valContext);
   // Ověření platnosti
   validateSignature(signature, valContext);
}

Trochu vysvětlení si ještě zaslouží třída MyKeySelector, která rozšiřuje třídu javax.xml.crypto.KeySelector a slouží k nalezení veřejného klíče vhodného k ověření podpisu. Ve třídě MyKeyselector musíme implementovat abstraktní metodu select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context). Naše implementace projde KeyInfo a vrátí klíč z prvního certifikátu, který potká.

for (Object o1 : keyInfo.getContent()) {
   if (o1 instanceof X509Data) {
      for (Object o2 : ((X509Data) o1).getContent()) {
         if (o2 instanceof X509Certificate) {
            return new SimpleKeySelectorResult(
            ((X509Certificate) o2).getPublicKey());
         }
      }
   }
}

Není-li podpis platný, je možné zjistit bližší podrobnosti o důvodu jeho neplatnosti. Nejprve si stručně zopakujeme, jak je XML podpis vytvořen.

  1. Vezmou se podepisované objekty určené svými URI (mohou to být celé dokumenty v libovolném formátu nebo části XML dokumentů) a po aplikaci kanonizace XML formátu a aplikaci nějakých transformací se z výsledku vypočte s použitím zadaného algoritmu otisk (hash). Každému z podepisovaných objektů odpovídá jeden element Reference, který obsahuje otisk a algoritmus, jímž byl otisk vypočten.
  2. Všechny reference se dají do elementu SignedInfo. Dále se tam přidá také informace o algoritmu podpisu a podobné drobnosti.
  3. Z elementu SignedInfo se vypočte otisk – algoritmus otisku je součástí algoritmu podpisu – a tento otisk se zašifruje pomocí soukromého klíče uživatele. Výsledný podpis se uloží do elementu SignatureValue.

Možné důvody neplatnosti podpisu jsou následující:

  • Některá z podepsaných referencí je neplatná – podepsaný otisk (hash) reference je odlišný od vypočteného otisku reference. To může být způsobeno tím, že po podepsání objektu někdo obsah URI změnil.
  • Otisk elementu SignedInfo získaný s použitím veřejného klíče z podpisu tohoto elementu je odlišný od vypočteného otisku. To může být způsobeno špatným klíčem nebo tím, že mezi podpisem a ověřováním podpisu byl obsah elementu SignedInfo nebo SignatureValue změněn.

Nyní se tedy nejprve podíváme na platnost podepsaných referencí:

for (Reference r : (List<Reference>) signature.getSignedInfo()
      .getReferences()) {
   boolean refValid = false;
   try {
      refValid = r.validate(valContext);
   } catch (XMLSignatureException e) {
      LOG.log(Level.SEVERE, „“, e);
   }
   LOG.info(„Platnost odkazu \““ + r.getURI() + „\“: “ + refValid);
}

A takto vypadá ověření platnosti podpisu elementu SignedInfo:

signatureValidity = signature.getSignatureValue().validate(valContext);

Můžete si stáhnout a otestovat zdrojový kód programu a tři XML soubory. Soubor test_podepsany.xml je platně podepsaný dokument, soubor test_podepsany_spatny_odkaz.xml má do obsahu podepisovaného elementu přidanou mezeru a test_podepsany_spatny_podpis.xml má za element ds:SignedInfo přidáno zalomení řádky. Uvidíte, jaký vliv mají tyto změny na platnost podpisu.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět