我是新成员,我等了两天才找到从android客户端到python服务器验证签名的解决方案。 首先我创建 key 对并从私钥生成签名。谢谢 pedrofb 我已经更新了完整的代码。在 python 服务器中验证完成。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
KeyPairGenerator keyPairGenerator = null;
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
//keyStore.deleteEntry("key1");
keyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
try {
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("key1", KeyProperties.PURPOSE_SIGN)
.setKeySize(2048)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
keyPairGenerator.initialize(builder.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null);
PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();
String publicKeyStr = Base64.encodeToString(publicKey.getEncoded(), Base64.NO_WRAP);
Log.d("Hahaha", publicKeyStr);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
String data = "haha";
signature.update(data.getBytes());
byte[] signatureBytes = signature.sign();
String signatureBase64 = Base64.encodeToString(signatureBytes, Base64.NO_WRAP);
Log.d("Hahaha", signatureBase64);
Signature verifySignature = Signature.getInstance("SHA256withRSA");
verifySignature.initVerify(publicKey);
verifySignature.update(data.getBytes());
boolean isVerify = verifySignature.verify(Base64.decode(signatureBase64, Base64.NO_WRAP));
Log.d("Hahaha", isVerify + "");
} catch (Exception e) {
e.printStackTrace();
}
}
public static String sha256(String rawString){
MessageDigest shaDigest;
byte[] data;
try {
data = rawString.getBytes("UTF-8");
shaDigest = MessageDigest.getInstance("SHA-256");
} catch (Exception e) {
return null;
}
shaDigest.update(data);
return toHex(shaDigest.digest());
}
public static String toHex(byte[] tmp) {
char hexDigits[] =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f'};
int nBytesLen = tmp.length;
char str[] = new char[nBytesLen * 2];
int k = 0;
for (byte byte0 : tmp) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
和服务器,我使用PKCS1_v1_5
来验证签名,它将从android客户端发送
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from base64 import b64decode
keyDER = b64decode(open('public.der').read())
key = RSA.importKey(keyDER)
message = "haha"
h = SHA256.new(message)
print h.hexdigest()
signature = b64decode(open('signature.der').read())
verifier = PKCS1_v1_5.new(key)
if verifier.verify(h, signature):
print "The signature is authentic."
else:
print "The signature is not authentic."
我已经检查了客户端和服务器的散列数据,它是相同的,但是 verifier.verify 返回 false,public.der
是变量 PublicKeyStr
和 signature 的值.der
是变量 SignatureBase64
的值
请帮助我。
最佳答案
[在评论中解决]你的 Java 代码中有几个错误
1) 在 Java 代码中,您使用 SHA256 对消息进行两次摘要并签署一个十六进制值,而不是二进制消息。
在 java 中,SHA256withRSA
执行多个操作,首先对消息应用 SHA256 摘要算法,然后应用 RSA PKCS #1 v1.5 算法。在 python(或其他语言)中,它可以分两步以编程方式完成
改变
signature.update(sha256(data).getBytes());
verifySignature.update(sha256(data).getBytes());
与
signature.update(data.getBytes());
verifySignature.update(data.getBytes());
2) 删除 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
。这是一个加密参数,不适用于数字签名
3) 您应该创建一次 key 对,而不是每次调用 onCreate
时
关于Android/Python 如何验证签名 SHA256withRSA 和 PKCS1 填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47749257/