package com.shephertz.app42.paas.customcode.sample;
import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.*;
import java.io.FileInputStream;
import java.security.*;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
//http://stackoverflow.com/questions/20877563/how-to-validate-windows-phone-in-app-purchase-with-java-on-server-side
//http://stackoverflow.com/questions/11985618/how-do-we-verify-windows-8-in-app-billing-receipt-on-the-server-side
//https://social.msdn.microsoft.com/forums/windowsapps/en-US/853f0713-b6e5-44c3-8131-aad136b81374/inapp-purchase-serverside-receipt-verification
//https://msdn.microsoft.com/library/windows/apps/jj206949(v=vs.105).aspx
//http://webservices20.blogspot.com/2013/06/validating-windows-mobile-app-store.html
public class SecurityWindowsPhone
{
public static boolean VerifyPurchase(String signedData) throws Exception
{
//Instantiate the document to be validated
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(signedData));
// Find Signature element
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0)
{
Log.Sev1("Windows Phone. Cannot find Signature element.");
return false;
}
// Create a DOM XMLSignatureFactory that will be used to unmarshal the
// document containing the XMLSignature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// Create a DOMValidateContext and specify a KeyValue KeySelector
// and document context
DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0));
// unmarshal the XMLSignature
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
// Validate the XMLSignature (generated above)
boolean coreValidity = signature.validate(valContext);
// Check core validation status
if (coreValidity == false)
{
Log.Sev1("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
Log.Sev1("signature validation status: " + sv);
// check the validation status of each Reference
Iterator i = signature.getSignedInfo().getReferences().iterator();
for (int j=0; i.hasNext(); j++)
{
boolean refValid = ((Reference) i.next()).validate(valContext);
Log.Sev1("ref["+j+"] validity status: " + refValid);
}
}
else
{
return true;
}
return false;
}
private static class KeyValueKeySelector extends KeySelector
{
public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context)
{
if (keyInfo == null)
{
Log.Sev1("Null KeyInfo object!");
return null;
}
SignatureMethod sm = (SignatureMethod) method;
List list = keyInfo.getContent();
for (int i = 0; i < list.size(); i++)
{
XMLStructure xmlStructure = (XMLStructure) list.get(i);
if (xmlStructure instanceof KeyValue)
{
PublicKey pk = null;
try
{
pk = ((KeyValue)xmlStructure).getPublicKey();
}
catch (KeyException ke)
{
Log.Sev1("KeyException");
return null;
}
// make sure algorithm is compatible with method
if (algEquals(sm.getAlgorithm(), pk.getAlgorithm()))
{
return new SimpleKeySelectorResult(pk);
}
}
}
Log.Sev1("No KeyValue element found!");
return null;
}
//@@@FIXME: this should also work for key types other than DSA/RSA
static boolean algEquals(String algURI, String algName)
{
if (algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1))
{
return true;
}
else if (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1))
{
return true;
}
else
{
return false;
}
}
}
private static class SimpleKeySelectorResult implements KeySelectorResult
{
private PublicKey pk;
SimpleKeySelectorResult(PublicKey pk)
{
this.pk = pk;
}
public Key getKey()
{
return pk;
}
}
}