2011-09-29 14 views
21

Desidero scrivere un servizio Web JAX-WS che firmi i miei messaggi SOAP utilizzando la raccomandazione http://www.w3.org/TR/xmldsig-core/.Firma richiesta SOAP JAX-WS

Con quello che ho trovato su internet ho scritto un gestore di JAX-WS (SOAPHandler<SOAPMessageContext>) che riesce a cambiare una copia della richiesta SOAP:

@Override 
public boolean handleMessage(SOAPMessageContext smc) { 
    Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); 
    SOAPMessage message = smc.getMessage(); 

    if (outboundProperty) { 
     try { 
      SOAPPart soapPart = message.getSOAPPart(); 
      SOAPEnvelope soapEnvelope = soapPart.getEnvelope(); 

      Source source = soapPart.getContent(); 

      Node root = null; 
      Document doc22 = null; 
      if (source instanceof DOMSource) { 
       root = ((DOMSource) source).getNode(); 
      } else if (source instanceof SAXSource) { 
       InputSource inSource = ((SAXSource) source).getInputSource(); 
       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
       dbf.setNamespaceAware(true); 
       DocumentBuilder db = null; 

       db = dbf.newDocumentBuilder(); 

       doc22 = db.parse(inSource); 
       root = (Node) doc22.getDocumentElement(); 
      } 

      XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); 

      Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null), 
        Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), 
        null, null); 

      SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, 
        (C14NMethodParameterSpec) null), 
        fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), 
        Collections.singletonList(ref)); 

      // Load the KeyStore and get the signing key and certificate. 
      KeyStore ks = KeyStore.getInstance("JKS"); 
      ks.load(new FileInputStream("client_keystore.jks"), "changeit".toCharArray()); 
      KeyStore.PrivateKeyEntry keyEntry = 
        (KeyStore.PrivateKeyEntry) ks.getEntry("client", new KeyStore.PasswordProtection("changeit".toCharArray())); 
      X509Certificate cert = (X509Certificate) keyEntry.getCertificate(); 
      // Create the KeyInfo containing the X509Data. 
      KeyInfoFactory kif2 = fac.getKeyInfoFactory(); 
      List x509Content = new ArrayList(); 
      x509Content.add(cert.getSubjectX500Principal().getName()); 
      x509Content.add(cert); 
      X509Data xd = kif2.newX509Data(x509Content); 
      KeyInfo ki = kif2.newKeyInfo(Collections.singletonList(xd)); 

      Element header = getFirstChildElement(root/*.getDocumentElement()*/); 
      DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), header /*doc.getDocumentElement()*/); 

      XMLSignature signature = fac.newXMLSignature(si, ki); 

      signature.sign(dsc); 

      //TODO: change this to update the SOAP message, not write it to disks 
      OutputStream os = new FileOutputStream("out.xml"); 
      TransformerFactory tf = TransformerFactory.newInstance(); 
      Transformer trans = tf.newTransformer(); 
      trans.transform(new DOMSource(root), new StreamResult(os)); 

     } catch (Exception ex) { 
      System.out.println(ex); 
     } 
    } 

    return true; 
} 

Ma io non riesco a capire come aggiornare il Richiesta SOAP?

+0

Does soapPart.setContent (nuovo DOMSource (root)) non funziona? Sto solo supponendo, non l'ho fatto da solo. –

+0

Sfortunatamente, questo svuota gli elementi bode e header. Grazie per la ricerca, però! – AndrewBourgeois

+0

Hai già trovato una soluzione a questo problema? Sono curioso visto che sto per fare qualcosa di simile –

risposta

6

Il modo più semplice è utilizzare la funzionalità integrata nel server delle applicazioni. Ad esempio: Securing JAX-WS Web services using message-level security with WebSphere App Server

Come configurare la firma su WAS è possibile trovare here.

E qui è WebLogic documentation about Configuring Message-Level Security.

+0

Riguarda la firma, non la protezione generale, che è ben compresa. Forse potresti fornire un link e/o un esempio più dettagliato? –

+0

@LukasEder: la sicurezza a livello di messaggio si riferisce anche alla firma. In questa pagina puoi trovare anche il collegamento alla configurazione della firma. L'ho aggiunto ora alla risposta. – zacheusz

+1

Molto bello. Soprattutto il secondo link! Grazie mille –

6

Sviluppo un SOAPHandler per Xml Digital Signature of Soap Request.

public class SOAPSecurityHandler implements 
     LogicalHandler<LogicalMessageContext> { 

    static final String KEYSTORE_FILE = "keystore_name.jks"; 
    static final String KEYSTORE_INSTANCE = "JKS"; 
    static final String KEYSTORE_PWD = "123456"; 
    static final String KEYSTORE_ALIAS = "keystore"; 

    public Set<QName> getHeaders() { 
     return Collections.emptySet(); 
    } 

    @Override 
    public boolean handleMessage(LogicalMessageContext smc) { 
     Boolean outboundProperty = (Boolean) smc 
       .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); 

     try { 

      if (outboundProperty) { 

       Source source = smc.getMessage().getPayload(); 

       Node root = null; 

       root = ((DOMSource) source).getNode(); 

       XMLSignatureFactory fac = XMLSignatureFactory 
         .getInstance("DOM"); 

       Reference ref = fac.newReference("", fac.newDigestMethod(
         DigestMethod.SHA1, null), Collections.singletonList(fac 
         .newTransform(Transform.ENVELOPED, 
           (TransformParameterSpec) null)), null, null); 

       SignedInfo si = fac.newSignedInfo(fac 
         .newCanonicalizationMethod(
           CanonicalizationMethod.INCLUSIVE, 
           (C14NMethodParameterSpec) null), fac 
         .newSignatureMethod(SignatureMethod.RSA_SHA1, null), 
         Collections.singletonList(ref)); 

       // Load the KeyStore and get the signing key and certificate. 
       KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE); 
       ks.load(new FileInputStream(KEYSTORE_FILE), 
         KEYSTORE_PWD.toCharArray()); 
       KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks 
         .getEntry(
           KEYSTORE_ALIAS, 
           new KeyStore.PasswordProtection(KEYSTORE_PWD 
             .toCharArray())); 
       X509Certificate cert = (X509Certificate) keyEntry 
         .getCertificate(); 
       // Create the KeyInfo containing the X509Data. 
       KeyInfoFactory kif2 = fac.getKeyInfoFactory(); 
       List x509Content = new ArrayList(); 
       x509Content.add(cert.getSubjectX500Principal().getName()); 
       x509Content.add(cert); 
       X509Data xd = kif2.newX509Data(x509Content); 
       KeyInfo ki = kif2.newKeyInfo(Collections.singletonList(xd)); 

       Element header = DOMUtils.getFirstChildElement(root); 
       DOMSignContext dsc = new DOMSignContext(
         keyEntry.getPrivateKey(), header); 

       XMLSignature signature = fac.newXMLSignature(si, ki); 

       signature.sign(dsc); 

      } 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     return true; 

    } 

    public boolean handleFault(SOAPMessageContext smc) { 
     // addDigitalSignature(smc); 
     return true; 
    } 

    // nothing to clean up 
    public void close(MessageContext messageContext) { 
    } 

    @Override 
    public boolean handleFault(LogicalMessageContext arg0) { 
     // TODO Auto-generated method stub 
     return false; 
    } 

} 

Penso che il problema nel codice di @AndrewBourgeois sia il modo di ottenere Source.

saluti,

+0

Dov'è la logica per DomUtils? – lordoku

1

Si può provare soapPart.saveChanges();

0

Dopo la linea di codice:

signature.sign(dsc); 

inserto questa dichiarazione:

soapMsg.saveChanges(); 

Sarà salvare le modifiche.