/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2016 All Rights Reserved.
 */
package cn.pluss.platform.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.binary.Base64;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * 进行签名验签的测试
 * 
 * @author jin.xie
 * @version $Id: XmlSignTest.java, v 0.1 2016年2月1日 上午10:51:39 jin.xie Exp $
 */
public class XmlSignUtil {
	
	//生产环境
    /** 公钥 RSA */
    public static final String RSA_publicKey  = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArohPusb9qwdDwnyTBxf+YnBEkeUfF+FJEzlK60TDKBnmQWk4zDsFRz6EroOvClxP998ffoTbyOuQJZHU9Hcb/YJaWBTaU7emcjxjV+L+6qBLZIzBdq4tH4dJXshKT8gd5ggQw00K2mwsdGI3ysFth8i8Gefu8wSduXT4yigLSWIyVLKa8lAFBe+//TZc9h76JJtCwLwULwryQLSfUo6d5bQhPsqcQ7Dnur8F+c5XdDmdkrFHXHlCuEHioR4dcOCvIf4UWq4inGKkwu7dJe5EOci7wLozLk+4F0bgryVX0kzxzusGkMF5UtwPjCmQnBI/mXFqnaHSr5iYBu0ElkwseQIDAQAB";
    /** 私钥 RSA */
    public static final String RSA_privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCuiE+6xv2rB0PCfJMHF/5icESR5R8X4UkTOUrrRMMoGeZBaTjMOwVHPoSug68KXE/33x9+hNvI65AlkdT0dxv9glpYFNpTt6ZyPGNX4v7qoEtkjMF2ri0fh0leyEpPyB3mCBDDTQrabCx0YjfKwW2HyLwZ5+7zBJ25dPjKKAtJYjJUspryUAUF77/9Nlz2Hvokm0LAvBQvCvJAtJ9Sjp3ltCE+ypxDsOe6vwX5zld0OZ2SsUdceUK4QeKhHh1w4K8h/hRariKcYqTC7t0l7kQ5yLvAujMuT7gXRuCvJVfSTPHO6waQwXlS3A+MKZCcEj+ZcWqdodKvmJgG7QSWTCx5AgMBAAECggEBAIAjbAYBWvRaXRFJTNkEcKvtKSrEQVPS/0ex5KNe1Ll4amjTy/OF0ZZA/ED0RWsh40Rk+EZ3XLXi3O34Z5UkbdRuA85vYOveBcIRPA6mhenUzgWAofl0X4SqWt/PxV9V2T2t5kC9K565lNnN0pB1+uiaJCAQ/IpHdVVX3+DsDX1N+FeAYCNRCDX2CTA+nsKe9wStMyU7xdHs/DKLjvCqrj3KvvEi2wp5I4eKbrxmTuz8RcsM9KyGQoyynijcK6DcOeiKeA+YAPTFJYAvDYtb0kjVP3Y+012Lg3rUYDmoOhsask+RwQddB188yBxu1VQYz1CcFs+te+LaZJdLLKlhz1ECgYEA3VhKpJpCPRpM03v+/FLCEwN0BWmc6FmyCQTk8dxZq7i/m/fhgjCVfS9WKABgMRd4pM3HnWwJImREpVRQAjLJrDEvnrhL+6ytn+OFLlX9+J6o3s0e3AW3Q9YFTMpzA8irrxDjHASXSi1VenkhdhzCMBLfzljIIW0EKV8e1Vr/WlsCgYEAydu8DIrpTmf5gM5isQNmIWa5ABP7rZSB+U3RKM95zHubmdYAIMAFhxnskZQaATqqGadAn4ZErG6xGVCqf8tGWVdRxAEsMxWOcbOGzv+7BJsl8rnFjiC0tgZZP4rXjTRRMM+WkMu5Dmnh4pkMpgGBCI8VsGGvjnvQ5aeoXeZfRLsCgYBXTJNkCFNHti/G2YjgC+Pvi9UerWDtYzMMy5lgqv/S8rLf2QxgRLkw/pKEhGFr+03h8idgt1OC9Pj+HBrSQyW3fE7D0vxk7t4ryZilhdT9UdZYyZ36OWbmomrEtrRWMbdE/OwWCNmCsA6el1fTNdSBEXCj+fA0/sI9bv7CN3mvMQKBgF29HuysLX/tpN39+bK+I1VrgInavctz5Y/CZVkQUD13QWEFEfYjde2xSjyojBL6DTfwhYAPOCDjHnblEOhbm4WtmRjuenoWjpmahwXlmBGhgtPfFTDVW1YUSKnLbiHyo6mJHSCMmplo7i7uaANHO4af+7B3DLGcKfwcCYHhk2g1AoGAVk69S0cgNDZed+/ZzJVrke3q0IlfBgU/p4dAytxxmzJ8rGRzclfGkwi+kaKIQwPvLYt2PjWaDt7G6wYwnUMBU4PoftmSCynl6u9FDm9kY8smkojH/e4k/N3yevfswtlIgE8JIX78CDxqZwVFV47pDe4Hji/wzCjGhrE1mfKTvNs=";
   
	
    
	//测试环境
//	/** 公钥 RSA */
//    public static final String RSA_publicKey  = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqOq41hcFZ1FdKMt6zXi1Qtm5s+KsOOLUPv6spkTAlw23yayObIv7aovo85Xsm0LRnmakEJBHpTY2UUlGyjKQ3GHFb9GN4ZyG6zyimVK3xBO0LSZhPN6NgcALqTFrlkhJ7eKq3aw53b9XhIPc3ZFvTFSqHR5Vl2xscNQkR776J9WJbK5MIW2bT7dRb9nJePMyHytKivbCdieGxkwiZCqGgkaHJTIXydPHVfgZ3zbluJ/8UucphS5/iUFsfdnOC/NiO9P01zjWWKo9qZpDad8/TJT6ZWby4pU3SrpNUeLm7ADW/M5UUXjA9B+BXGkK6D8O/CU841hM2aLlfIi4TpO1DQIDAQAB";
//    /** 私钥 RSA */
//    public static final String RSA_privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCo6rjWFwVnUV0oy3rNeLVC2bmz4qw44tQ+/qymRMCXDbfJrI5si/tqi+jzleybQtGeZqQQkEelNjZRSUbKMpDcYcVv0Y3hnIbrPKKZUrfEE7QtJmE83o2BwAupMWuWSEnt4qrdrDndv1eEg9zdkW9MVKodHlWXbGxw1CRHvvon1YlsrkwhbZtPt1Fv2cl48zIfK0qK9sJ2J4bGTCJkKoaCRoclMhfJ08dV+BnfNuW4n/xS5ymFLn+JQWx92c4L82I70/TXONZYqj2pmkNp3z9MlPplZvLilTdKuk1R4ubsANb8zlRReMD0H4FcaQroPw78JTzjWEzZouV8iLhOk7UNAgMBAAECggEAcyeBVsJe9q5rOhJt54Gw4KAZj16qViWc2E06WidovBGoB57RgYJ0pZA4AUdmA/qreLc0he42SZUJtqSj09hhFTy6Ip7TgMj62Yd+udPAX7WnEVim1Z5LDfdke7WrSMqDS99I6NIuqNN20GosuggZvgnB5xP0+cP6sXiOOzMgyuIU0Y44SGwUiDn70O1FjJd6ydbhRnsRLyrWmj+mDDGwVBGyJdRKDKcp7MGhCHmqhKFr4IETAvhsjyjF6msmySk/oxsDZGCXx/xNDFIOPVzbjqMOZfShHX3hvDmla51yCmWB7rioco5u232bxZPNNLqf4LULa/Npb0piBbdSvoAkKQKBgQDpyL1J2rr2UbrzW/G+uAipevNnLJn35cX1FW/IJmZS7zUzz6hE+xeUo7iDCKYtqsdsnxMND68jcKGu7NO9pGi+4NlakHPBxLpudZmkXJWVVCFqKwvSr0z8dbnIQNvSyppWInZZtApZGgclODhAPqe7RSIOk7gfxnAHzFDEoZRc7wKBgQC49/VmHx0Zsl8JM5BE0jEMoK3cNbxPeSENR0HoSUAorXiK4eUueAvi0nPzPNzdDlbMonBJrdNW9u+hxQuDFKccb/gxeAM50Z/Qb+5FXvps/vUB0qxnY0Ud/6W8ycb5f7Scfhm79Rfx63crwFr+V9CrE50VwixAogAC66z3C8PFwwKBgQDWdefvUFAilmdByB7ELl8D2l5fT3MPDiHC6p9YpKFJg1by6JQcc5r2KBqjWdPYIMpAwszfNnwbrr8jkO3NEn1kXTAG/Gy0XJwJqKgBN02hZ0Qf+M0nwNiG4BhFs/n0LRfX3FVlhNvZUR8s7Z3skY2dJZZQeQJ3Y7px7ORK/U700QKBgHPYqNOa7QjpFGiY6OSRmLLF994Rsa08uhys4nRvXEWdz/9716Qw/5DCKmjPV60DjA2CNz2OJu0yn4b7+GfSiY9f+VxSRAL0bVmIoPzFraaSUDUiE2R1YhaVxOHmpj4hY6cXu1pi+OpFhU/bm6Hssf5TvHCiOkEItWUe4iegMK8tAoGBALZwjzK23WqNzHG8wnFCF3chP5V4k/ttpO7lnS70PlmkTDH1VS96C/fY9VEvJPAFiRz4p3Iy06e/DGyOE2ZFPts3ybrloC53JGAZgXrvVhDHMx8N5yEM71fhM7horRQuWvNiNPcuqi4Fz9IuQ1apbgwrBcapAaNF5OTVh1CxK22w";

    
    
    
    /** 网商银行测试环境公钥 RSA */
    public static String bank_RSA_publicKey  = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOb4B1dnwONcW0RoJMa0IOq3O6jiqnTGLUpxEw2xJg+c7wsb6DBy5CAoR0w2ZjZ/BjKxGIQ+DoDg3NsHJeyuEjNF0/Ro/R5xVpFC5z4cBVSC2/gddz4a1EoGDJewML/Iv0yIw7ylB86++h23nRd079c5S9RZXurBfnLW2Srhqk2QIDAQAB";
    private static final String XML_ALGORITHM      = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
    private static final String CHARSET            = "UTF-8";
    private static final String RESPONSE_TAG_NAME  = "response";
    /**
     * 签名- XML
     * 
     * @throws Exception
     */
    public static String sign(String xmlContent) throws Exception {
        Document doc = parseDocumentByString(xmlContent);
        PrivateKey privateKey = SignatureUtils.getPrivateKey(RSA_privateKey);
        String result = SignatureUtils.signXmlElement(privateKey, doc, "request",
            XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, 2);
        return result;
    }
    
    
    public static String signResponse(String xmlContent) throws Exception {
        Document doc = parseDocumentByString(xmlContent);
        PrivateKey privateKey = SignatureUtils.getPrivateKey(RSA_privateKey);
        String result = SignatureUtils.signXmlElement(privateKey, doc, "response",
            XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, 2);
        return result;
    }

    /**
     * 验签 - XML
     * 
     * @throws Exception
     */
    public static boolean verify(String xmlContent) throws Exception {
        Document doc = parseDocumentByString(xmlContent);
        PublicKey publicKey = SignatureUtils.getPublicKey(RSA_publicKey);
        //PublicKey publicKey = SignatureUtils.getPublicKey(RSA_publicKey);
        return SignatureUtils.verifyXmlElement(publicKey, doc);
    }

    /**
     * 解析XML
     * 
     * @param xmlContent
     * @return
     * @throws SAXException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    private static Document parseDocumentByString(String xmlContent) throws SAXException,
                                                                    IOException, Exception {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);// 否则无法识别namespace
        return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlContent)));
    }
    
    public static void main(String[] args) throws Exception{
        bank_RSA_publicKey  = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg0HDiStmHDTHOAptH5Jlp4sg03Czir/My4vvFK/7Un/zyuyTa3aHqav4+GqPhGJcIGIiLVuP/tCCEU4Edl3LC0bNxUBdnPv7k3HDoonWmqD/EhBXKe/GD/Dwrn90VfP1s314ykj8Z2T06l3LYb5F44L8JOgVrqJOe2FMXvsEt6sik2jnMdq2rJLeda1QLQMKnla4t2wVK1DoC0eu+ry3Oc2BWhYwU1bwWaX71mROH0Q3jXj3FFLdGp2trrnkKnQzNfGHjRChxTWl3CgiWbFd/Ejgp0/cpovq3cAJtxD7HdtwOibiI9u6owyWFM6C2pMjsuI23WofRyD9UQoU0UToiwIDAQAB";
        File file = new File("d:/sign.txt");
        InputStream in  = new FileInputStream(file);
        byte[] b = new byte[in.available()];
        in.read(b);
        String xml =new String(b);
        in.close();
       boolean result = verify(xml);
       System.out.println(result);
    }
    
    

    public void beforeTest() {
        org.apache.xml.security.Init.init();
    }
    
    public static String xmlSign(String xmlSource) throws Exception {
    	org.apache.xml.security.Init.init();
        PrivateKey privateKey = getPrivateKey(RSA_privateKey.getBytes());
        System.out.println(privateKey);
//        String xmlSource = "<document><request><head><appid>2017060700000025</appid><function>ant.mybank.yulibao.accountopen.query</function><inputCharset>UTF-8</inputCharset><reqMsgId>e08d3570-af06-42ca-9486-6b553e4d76d5</reqMsgId><respTime>20170613124952</respTime><version>1.0.0</version></head><body><fundCode>001529</fundCode><isvOrgId>202210000000000001055</isvOrgId><merchantId>2088611330087634</merchantId></body></request></document>";
        String xmlWithSign = signResponseMessage(xmlSource, privateKey);
		return xmlWithSign;
    }
    
    
    
    /**
     * 读取私钥
     * 
     * @param keyData 私钥数据
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(final byte[] keyData) throws Exception {

        byte[] encodedKey = Base64.decodeBase64(keyData);
        final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
        final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }
    
    
    /**
     * 私钥签名应答报文
     * 
     * @param responseMessage
     * @param privateKey
     * @return
     * @throws Exception 
     */
    public static String signResponseMessage(String responseMessage, PrivateKey privateKey)
                                                                                           throws Exception {
        return signXmlMessage(responseMessage, RESPONSE_TAG_NAME, privateKey);
    }
    /**
     * 私钥签名
     * 
     * @param xmlMessageSource 待签名的XML
     * @param elementTagName
     * @param privateKey
     * @return
     * @throws Exception 
     */
    public static String signXmlMessage(String xmlMessageSource, String elementTagName,
            PrivateKey privateKey) throws Exception {

		Document xmlDocument = getDocument(xmlMessageSource);
		XMLSignature xmlSignature = new XMLSignature(xmlDocument, xmlDocument.getDocumentURI(),
		XML_ALGORITHM);
		
		NodeList nodeList = xmlDocument.getElementsByTagName(elementTagName);
		if (nodeList == null || nodeList.getLength() != 1) {
		throw new Exception("Document element with tag name " + elementTagName + " not fount");
		}
		Node elementNode = nodeList.item(0);
		elementNode.getParentNode().appendChild(xmlSignature.getElement());
		
		Transforms transforms = new Transforms(xmlDocument);
		transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
		xmlSignature.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
		
		xmlSignature.sign(privateKey);
		
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		XMLUtils.outputDOM(xmlDocument, os);
		return os.toString(CHARSET);
   }
    
    /**
     * 解析XML字符串
     * 
     * @param xmlMessageSource
     * @return
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    private static Document getDocument(String xmlMessageSource)
                                                                throws ParserConfigurationException,
                                                                SAXException, IOException,
                                                                UnsupportedEncodingException {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document xmlDocument = builder.parse(new InputSource(new ByteArrayInputStream(
            xmlMessageSource.getBytes(CHARSET))));
        return xmlDocument;
    }
    
    
    
    

}