java - 如何在服务器端使用 Java 验证 Windows Phone 应用内购买?

标签 java windows-phone-8 in-app-purchase windows-phone

根据示例:http://code.msdn.microsoft.com/wpapps/In-app-purchase-receipt-c3e0bce4

应验证的 xml 文件是:

<?xml version="1.0"?>
<Receipt Version="1.0" CertificateId="A656B9B1B3AA509EEA30222E6D5E7DBDA9822DCD" xmlns="http://schemas.microsoft.com/windows/2012/store/receipt">
<ProductReceipt PurchasePrice="$20.89" PurchaseDate="2012-11-30T21:32:07.096Z" Id="2f9c5c8f-3e1d-4fc7-a871-ac58f7e78053" AppId="3ec6cd9a-ca82-4d38-bfdf-ecafdb35a738" ProductId="Test" ProductType="Consumable" PublisherDeviceId="Test" MicrosoftProductId="59ef70aa-7099-4679-889e-f21919bfd2c6" />
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
  <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
  <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
  <Reference URI="">
    <Transforms>
      <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
    </Transforms>
    <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
    <DigestValue>FyFb1HGm+yeOIjt18M6TPD4Qzeu469vwDbQs7w72mdA=</DigestValue>
  </Reference>
</SignedInfo>    
 <SignatureValue>noct5CYBtRwBxVxkUeZIzeDyruLGVBqBMuFuBytpouPLACnQ5dbzdRvWX4XN67IUo0J2FW8DoYcMbf3sAS+PeKKV8SLnU+l8K1hWEbbbugHZezStTzwwkYcZuCTnAk7BYO0aiZWuXm9GiZGT9iyXsYtU1/u87L+llnVibU/m7gV8tD3vG0tVkjzV20C8666mHUsY/jxeq3ed7YY9CT0SDrh5PeL4ESaopBLcncHo/e6lcjyoKbO3e6YuIpsi8DVueeKNhpTlwa5yc0O3qzc5SGnT4Kbhj9NBEXf15/oTaLlg7lJhnQZ0mY+yR8vc4D0SkqD6e5Uc4u64hnu+g3Hphg==</SignatureValue>
</Signature>
</Receipt>

我可以从 .cer 文件获取公钥:

<Modulus>29AnpbVajjiCYsDRoAK+2dCuKwGLBwmtaEhTyV7aJM9d+G4tp3ifGxyCgJ09JMGt8VYlMcKd/jmXdrdj7IQ79LnGeXbrOlgp7gMPzB1QKHYC0DKmIinjO1UyIt0tbN4lf+umPCj+4x9wphNlwlzkGnvm0FLshrE4rdEnFO/jKHzg6DYbzjbOeJ/ytVlZ3LdsFgaZ/BhgEvpdpszhdI12zEo7c+8Liu+xnsnRvsfaWi7hyMgrKO7Yzqr/5b6JeSCF9FZwuRtrwunVkejJDS+Rcy157ErkIKGBFGVz5VyYbJr+qMsnp2j9D6WCWXAiy1YtS9oXFaPAdgyBINyN7fHIfw==</Modulus>
<Exponent>AQAB</Exponent>

我想在我的 java 服务器上进行验证。我在这里得到了示例代码:http://docs.oracle.com/javase/7/docs/technotes/guides/security/xmldsig/Validate.java 。 然后我根据: Validate XML signature JAVA 对 xml 文件做了一些小的更改

<?xml version="1.0"?>
<Receipt Version="1.0" CertificateId="A656B9B1B3AA509EEA30222E6D5E7DBDA9822DCD" xmlns="http://schemas.microsoft.com/windows/2012/store/receipt">
 <ProductReceipt PurchasePrice="$20.89" PurchaseDate="2012-11-30T21:32:07.096Z" Id="2f9c5c8f-3e1d-4fc7-a871-ac58f7e78053" AppId="3ec6cd9a-ca82-4d38-bfdf-ecafdb35a738" ProductId="Test" ProductType="Consumable" PublisherDeviceId="Test" MicrosoftProductId="59ef70aa-7099-4679-889e-f21919bfd2c6" />
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
  <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
  <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
  <Reference URI="">
    <Transforms>
      <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
    </Transforms>
    <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
    <DigestValue>FyFb1HGm+yeOIjt18M6TPD4Qzeu469vwDbQs7w72mdA=</DigestValue>
  </Reference>
</SignedInfo>

<SignatureValue>noct5CYBtRwBxVxkUeZIzeDyruLGVBqBMuFuBytpouPLACnQ5dbzdRvWX4XN67IUo0J2FW8DoYcMbf3sAS+PeKKV8SLnU+l8K1hWEbbbugHZezStTzwwkYcZuCTnAk7BYO0aiZWuXm9GiZGT9iyXsYtU1/u87L+llnVibU/m7gV8tD3vG0tVkjzV20C8666mHUsY/jxeq3ed7YY9CT0SDrh5PeL4ESaopBLcncHo/e6lcjyoKbO3e6YuIpsi8DVueeKNhpTlwa5yc0O3qzc5SGnT4Kbhj9NBEXf15/oTaLlg7lJhnQZ0mY+yR8vc4D0SkqD6e5Uc4u64hnu+g3Hphg==</SignatureValue>
<KeyInfo>
<X509Data>  <X509Certificate>MIIDFDCCAgCgAwIBAgIQrih3cQuSeL1CgpLFusfJsTAJBgUrDgMCHQUAMB8xHTAbBgNVBAMTFElhcFJlY2VpcHRQcm9kdWN0aW9uMB4XDTEyMDIxNzAxMTYyNFoXDTM5MTIzMTIzNTk1OVowHzEdMBsGA1UEAxMUSWFwUmVjZWlwdFByb2R1Y3Rpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb0CeltVqOOIJiwNGgAr7Z0K4rAYsHCa1oSFPJXtokz134bi2neJ8bHIKAnT0kwa3xViUxwp3+OZd2t2PshDv0ucZ5dus6WCnuAw/MHVAodgLQMqYiKeM7VTIi3S1s3iV/66Y8KP7jH3CmE2XCXOQae+bQUuyGsTit0ScU7+MofODoNhvONs54n/K1WVnct2wWBpn8GGAS+l2mzOF0jXbMSjtz7wuK77GeydG+x9paLuHIyCso7tjOqv/lvol5IIX0VnC5G2vC6dWR6MkNL5FzLXnsSuQgoYEUZXPlXJhsmv6oyyenaP0PpYJZcCLLVi1L2hcVo8B2DIEg3I3t8ch/AgMBAAGjVDBSMFAGA1UdAQRJMEeAEHGLK3BRpCWDa2vU50kI73ehITAfMR0wGwYDVQQDExRJYXBSZWNlaXB0UHJvZHVjdGlvboIQrih3cQuSeL1CgpLFusfJsTAJBgUrDgMCHQUAA4IBAQC4jmOu0H3j7AwVBvpQzPMLBd0GTimBXmJw+nruE+0Hh/0ywGTFNE+KcQ21L4v+IuP8iMh3lpOcPb23ucuaoNSdWi375/KxrW831dbh+goqCZP7mWbxpnSnFnuV+R1VPsQjdS+0tg5gjDKNMSx/2fH8krLAkidJ7rvUNmtEWMeVNk0/ZM/ECinobMSSwbqUuc9Qql9T1epe+xv34a6eek+m4W0VXnLSuKhQS5jdILsyeJWHROZF5mrh3DQuS0Ll5FzKmJxHf0hyXAo03SSA+x3JphAU4oYbkE9nRTU1tR6iq1D9ZxfQmvzmIbMfyJ/y89PLs/ewHopSK7vQmGFjfjIl</X509Certificate></X509Data>
    <KeyValue>
    <RSAKeyValue>
        <Modulus>29AnpbVajjiCYsDRoAK+2dCuKwGLBwmtaEhTyV7aJM9d+G4tp3ifGxyCgJ09JMGt8VYlMcKd/jmXdrdj7IQ79LnGeXbrOlgp7gMPzB1QKHYC0DKmIinjO1UyIt0tbN4lf+umPCj+4x9wphNlwlzkGnvm0FLshrE4rdEnFO/jKHzg6DYbzjbOeJ/ytVlZ3LdsFgaZ/BhgEvpdpszhdI12zEo7c+8Liu+xnsnRvsfaWi7hyMgrKO7Yzqr/5b6JeSCF9FZwuRtrwunVkejJDS+Rcy157ErkIKGBFGVz5VyYbJr+qMsnp2j9D6WCWXAiy1YtS9oXFaPAdgyBINyN7fHIfw==</Modulus>
        <Exponent>AQAB</Exponent>
    </RSAKeyValue>
    </KeyValue>
</KeyInfo>
</Signature>
</Receipt>

但是验证失败。我找不到解决方案。请帮忙。

我的java代码如下:

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


/**
 * This is a simple example of validating an XML
 * Signature using the JSR 105 API. It assumes the key needed to
 * validate the signature is contained in a KeyValue KeyInfo.
 */
public class ValidateXMLwithCertificate {

//
// Synopsis: java Validate [document]
//
//    where "document" is the name of a file containing the XML document
//    to be validated.
//
public static void main(String[] args) throws Exception {

    // Instantiate the document to be validated
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    Document doc =
        dbf.newDocumentBuilder().parse(new FileInputStream("./ReceiptSHA256.xml"));
    // Find Signature element
    NodeList nl =
        doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
    if (nl.getLength() == 0) {
        throw new Exception("Cannot find Signature element");
    }
    NodeList nl2 =
            doc.getElementsByTagNameNS(XMLSignature.XMLNS, "KeyInfo");
        if (nl2.getLength() == 0) {
            throw new Exception("Cannot find KeyInfo element");
        }
    // 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) {
        System.err.println("Signature failed core validation");
        boolean sv = signature.getSignatureValue().validate(valContext);
        System.out.println("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);
            System.out.println("ref["+j+"] validity status: " + refValid);
        }
    } else {
        System.out.println("Signature passed core validation");
    }
}

/**
 * KeySelector which retrieves the public key out of the
 * KeyValue element and returns it.
 * NOTE: If the key algorithm doesn't match signature algorithm,
 * then the public key will be ignored.
 */
private static class KeyValueKeySelector extends KeySelector {
    public KeySelectorResult select(KeyInfo keyInfo,
                                    KeySelector.Purpose purpose,
                                    AlgorithmMethod method,
                                    XMLCryptoContext context)
        throws KeySelectorException {

        System.out.println(keyInfo + " " + method);


        if (keyInfo == null) {
            throw new KeySelectorException("Null KeyInfo object!");
        }

        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();
                    System.out.println(pk);
                } catch (KeyException ke) {
                    throw new KeySelectorException(ke);
                }
                System.out.println(sm.getAlgorithm() + " -------- " +  pk.getAlgorithm());
                // make sure algorithm is compatible with method
                if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
                    return new SimpleKeySelectorResult(pk);
                }
            }
        }
        throw new KeySelectorException("No KeyValue element found!");
    }

    //@@@FIXME: this should also work for key types other than DSA/RSA
    static boolean algEquals(String algURI, String algName) {

        XMLSignatureFactory factory = XMLSignatureFactory.getInstance();
        SignatureMethod sm = null;
        try {
            sm = 
                    factory.newSignatureMethod
                        ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", 
                         (SignatureMethodParameterSpec) null);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("algURI:" + algURI + " algName:" + algName + " SHa1:" + sm.getAlgorithm());
        if (algName.equalsIgnoreCase("DSA") &&
            algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
            return true;
        } else if (algName.equalsIgnoreCase("RSA") &&
                   algURI.equalsIgnoreCase(sm.getAlgorithm())) {
            System.out.println("will return true");
            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; }
}
}

最佳答案

删除“>”和“<”之间的所有空格或“\n”后,终于验证成功了。

关于java - 如何在服务器端使用 Java 验证 Windows Phone 应用内购买?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20877563/

相关文章:

java - 从 Android 项目创建 jar

java - 将 java.util.Date 转换为 LocalDate

windows-phone-8 - 如何将 Windows Phone 8 应用程序移植到 Windows Phone 8.1?

Android - 亚马逊应用内购买

android - 如何从付费应用迁移到应用内购买?

ios - 从 iOS 上没有 IAP 的版本升级后,为用户帐户保留 IAP

java - 计算两个 double 的平均值?

java - 将多个参数作为一个 http 查询参数传递的最佳方法是什么?

wpf - 设置 Style 属性后,为什么我无法覆盖控件中的属性?

c# - MVVM模式实现