如何使用JavaJDK1.7在我的第三方服务器上验证GKLocalPlayer?

如何使用JavaJDK1.7在我的第三方服务器上验证GKLocalPlayer?,java,ios,authentication,verification,Java,Ios,Authentication,Verification,使用Apple的Game Center身份验证步骤,下面的验证逻辑已使用Java实现。然而,这总是失败的 import java.net.URL; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import jav

使用Apple的Game Center身份验证步骤,下面的验证逻辑已使用Java实现。然而,这总是失败的

import java.net.URL;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.MessageDigest;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.SecureRandom;

import java.security.Signature;

import java.security.cert.Certificate;

import java.security.cert.CertificateFactory;

import java.security.spec.AlgorithmParameterSpec;

import java.util.Arrays;

import javax.crypto.Cipher;

import javax.xml.bind.DatatypeConverter;

public class Verifier {

    public static void main(String[] args) {

        verify1();   

    }



    public static void verify1() {

        try {

            byte[] playerID = "G:90082947".getBytes("UTF-8");

            byte[] bundleID = "com.appledts.GameCenterSamples".getBytes("UTF-8");



            long ts = 1392078336714L;

            final ByteBuffer tsByteBuffer = ByteBuffer.allocate(8);

            tsByteBuffer.order(ByteOrder.BIG_ENDIAN);

            tsByteBuffer.putLong(ts);           

            byte[] timestamp = tsByteBuffer.array();



            byte[] salt = DatatypeConverter.parseBase64Binary("xmvbZQ==");



            byte[] sigToCheck = DatatypeConverter.parseBase64Binary("AmyNbm+7wJOjXv6GXI/vAEcl6gSX1AKxPr3GeExSYCiaxVaAeIvC23TWtp1/Vd/szfq1r1OzwrvkHeSSiskWMsMXaGQWUmiGtCnf9fqBU75T5PwNLCj4H9Nd5QENCMV/CFgVyGEi4X6Wlp18kqJPk/ooS6jLJwcWIe6DyrR1bQHl6YzKTfB4ACl2JEccBDz8dArKTrh4vFcQF4a+DtERm283Y2ue1DwG8lqWrYhsRO5v7vrW3lVpn5t25QXc+Y35zJ/il+lZJxKAgASwrKaq3G8RStdkeXCER23fSYhTmbLFqkFRWnmzu38hmLt5/iivUbm8NgELXP0SyQoYLMvfmA==");



            ByteBuffer dataBuffer = ByteBuffer.allocate(playerID.length+bundleID.length+8+salt.length)

                .put(playerID)

                .put(bundleID)

                .put(timestamp)

                .put(salt);





            Certificate cert = CertificateFactory.getInstance("X.509")

                    .generateCertificate(new URL("https://sandbox.gc.apple.com/public-key/gc-sb.cer").openConnection().getInputStream());





            Signature sig = Signature.getInstance("SHA1withRSA");

            sig.initVerify(cert);



            sig.update(dataBuffer);



            final boolean verify = sig.verify(sigToCheck);

            System.out.println("signature verifies: " + verify);                            



        } catch (Exception e) {            

            e.printStackTrace();

        }

    }        

}
从iOS 7客户端向服务器传输数据时没有丢失位。通过将二进制位从xCode和Java写入文件,生成它们的十六进制,并查看是否存在差异(注意,差异仅显示文件名差异),可以验证这一点:


除了验证数字签名或上面发布的解决方案中的任何漏洞之外,还有其他方法吗?

仍然不确定signature.verify失败的原因,但现在找到了解决方法:解密签名以进行检查,并从解密的哈希中取消SHA1哈希,并与数据缓冲区摘要进行比较。如果两者匹配,则验证game center用户凭据,否则不验证。请参见下面的示例代码

 final MessageDigest md = MessageDigest.getInstance("SHA-1");
 byte[] digest = md.digest(dataBuffer.array());

 Cipher c2 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
 c2.init(Cipher.DECRYPT_MODE, cert.getPublicKey());
 byte[] decrypted2 = c2.doFinal(sigToCheck);
 final byte[] unpaddedSHA1 = Utils.unpadSHA1(decrypted2);

 System.out.println("signature verifies: " + Arrays.equals(digest, unpaddedSHA1));
其中,upadSHA1的定义如下:

private static final String SHA1_PAD = "3021300906052b0e03021a05000414";
private static final byte[] sha1pad = DatatypeConverter.parseHexBinary(SHA1_PAD);

public static byte[] unpadSHA1(byte[] padded) throws BadPaddingException {
    int k = 0;

    if (padded.length < sha1pad.length) {
        throw new BadPaddingException("Padding string too short");
    }
    while (true) {
        if (padded[k] != sha1pad[k]) {
            break;
        }
        k++;                  
        if (k == sha1pad.length) {
            break;
        }
    }
    int n = padded.length - k;
    if (n > 256) {
        throw new BadPaddingException("Padding string too short");
    }
    byte[] data = new byte[n];
    System.arraycopy(padded, padded.length - n, data, 0, n);
    return data;
}
私有静态最终字符串SHA1_PAD=“302130090602B0E03021A05000414”; 私有静态最终字节[]sha1pad=DatatypeConverter.parseHexBinary(SHA1_PAD); 公共静态字节[]unpadSHA1(字节[]已填充)引发BadPaddingException{ int k=0; if(填充长度256){ 抛出新的BadPaddingException(“填充字符串太短”); } 字节[]数据=新字节[n]; 数组复制(padded,padded.length-n,data,0,n); 返回数据; }
问题似乎在于传递给Signature.update()的ByteBuffer。如果通过更改

sig.update(dataBuffer);


核查似乎成功了。基于,我怀疑这是因为它试图读取您在缓冲区中写入的最后一个位置,却找不到任何数据。

很好!在调用
sig.update(dataBuffer)
和验证成功之前,需要调用
dataBuffer.rewind()
。谢谢
private static final String SHA1_PAD = "3021300906052b0e03021a05000414";
private static final byte[] sha1pad = DatatypeConverter.parseHexBinary(SHA1_PAD);

public static byte[] unpadSHA1(byte[] padded) throws BadPaddingException {
    int k = 0;

    if (padded.length < sha1pad.length) {
        throw new BadPaddingException("Padding string too short");
    }
    while (true) {
        if (padded[k] != sha1pad[k]) {
            break;
        }
        k++;                  
        if (k == sha1pad.length) {
            break;
        }
    }
    int n = padded.length - k;
    if (n > 256) {
        throw new BadPaddingException("Padding string too short");
    }
    byte[] data = new byte[n];
    System.arraycopy(padded, padded.length - n, data, 0, n);
    return data;
}
sig.update(dataBuffer);
sig.update(dataBuffer.array());