使用PHP/Python验证ECDSA签名
Using PHP/Python to verify ECDSA signature
我使用 spongycastle 库编写代码以在 Android 上生成 ECDSA 签名。然后我将签名字符串和 public-key 发送到服务器 (Ubuntu 16.04) 并尝试使用 php 和 python 来验证这个签名。
我在我的 android 应用程序上进行测试验证。效果很好。
我使用 php-openssl 扩展,Python 我使用 ecdsa 0.13。但是,这两个都失败了。我再试一次,使用openssl命令,它也无法验证。
我不知道,我哪里错了。
为什么ECDSA验证失败?
这是我的代码:
生成签名(android):
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1");
try {
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA","SC");
g.initialize(spec, new SecureRandom());
KeyPair keyPair = g.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
///write public key to pem file
......
FileOutputStream fileOutputStream1 = new FileOutputStream(file1);
StringWriter writer1 = new StringWriter();
PemWriter pemWriter1 = new PemWriter(writer1);
pemWriter1.writeObject(new PemObject("PUBLIC KEY",publicKey.getEncoded()));
pemWriter1.flush();
pemWriter1.close();
String publickeyPem = writer1.toString();
fileOutputStream1.write(publickeyPem.getBytes());
fileOutputStream1.close();
} catch (Exception e) {
e.printStackTrace();}
......
//Sign and veryfied
String chuoi = txtChuoi.getText().toString();
byte[] chuoiInput = chuoi.getBytes("UTF-8");
Signature sig = Signature.getInstance("NONEwithECDSA","SC");
sig.initSign(privateKey);
sig.update(chuoiInput);
///SIGN
byte[] signatureBytes = sig.sign()
txtMaHoa.setText(Base64.encodeToString(signatureBytes,Base64.DEFAULT));
sig.initVerify(publicKey);
sig.update(chuoiInput);
///VERIFIED
txtGiaiMa.setText(sig.verify(signatureBytes)+"");
///Write string sign in txtMahoa to file
.......
我有签名和public密钥的输出是:
(Signature string) MEYCIQC7Hz631IFGsUOogcRLeN99uM9hWgLr+LGzuJvR/6nBrgIhAMXgZcvXyMRCAELXlNNS1a9j iAT1x0q2C5Mdu+2aZKtN
(公钥)
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEj07XEM+ulPyrdsfAf9prN2L2dNUd /Yy0rABcFdueAwYUf86f8Cc93Ws6sxzIvf2iKOapFby7EjHewjhLM/z7Qg==
-----END PUBLIC KEY-----
使用PHP验证:
$pubkeyid = openssl_pkey_get_public("/var/www/html/ca.pem");
$data = "nguyen tran thanh Lam";
$signature =
"MEYCIQC7Hz631IFGsUOogcRLeN99uM9hWgLr+LGzuJvR/6nBrgIhAMXgZcvXyMRCAELXlNNS1a9jiAT1x0q2C5Mdu+2aZKtN";
$ok = openssl_verify($data, $signature, $pubkeyid);
if ($ok == 1) {
echo "good";
} elseif ($ok == 0) {
echo "bad";
} else {
echo "ugly, error checking signature";
}
?>
Comment: ... but the verify function don't return anything. So I not sure
如果crypto.verify(...
没有抛出错误,这意味着验证成功。请参阅下面更新的代码。
使用错误的证书、签名或数据进行验证。
Comment: what do you think about add base64.b64decode. ... because in android code I have this linetxtMaHoa.setText(Base64.encodeToString(signatureBytes,Base64.DEFAULT));
PEM
已经是base64
,检查上面的.setText(Base64...
是否可以省略。现在我已经用 base64.b64decode
.
更新了下面的代码
Question: Example ---- b"sha256"? "b" is a binary (built-in funtion or I must convert string sha256 to binary?)
仅适用于我的字符串 (digest-names),例如:
import pem, base64
from OpenSSL import crypto
signature = b"MEYCIQC7Hz631IFGsUOogcRLeN99uM9hWgLr+LGzuJvR/6nBrgIhAMXgZcvXyMRCAELXlNNS1a9jiAT1x0q2C5Mdu+2aZKtN"
# As Creator of the Signatur do additional base64 encoding!
signature = base64.b64decode(signature)
data = "nguyen tran thanh Lam"
certificate = pem.parse_file('Android.pem')[0]
cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate.as_bytes())
try:
crypto.verify(cert=cert, signature=signature, data=data, digest='sha256')
except crypto.Error as exp:
print('crypto.Error:{}'.format(exp.args))
>>>crypto.Error:([('', 'ECDSA_do_verify', 'bad signature')],)
Note: Throws crypto.Error
because not using the certificate
which created the signature
.
OpenSSL.crypto.verify(certificate, signature, data, digest)
Verify the signature for a data string.
certificate is a X509 instance corresponding to the private key which generated the signature.
signature is a str instance giving the signature itself.
data is a str instance giving the data to which the signature applies.
digest is a str instance naming the message digest type of the signature, for example b"sha256".
New in version 0.11.
我使用 spongycastle 库编写代码以在 Android 上生成 ECDSA 签名。然后我将签名字符串和 public-key 发送到服务器 (Ubuntu 16.04) 并尝试使用 php 和 python 来验证这个签名。
我在我的 android 应用程序上进行测试验证。效果很好。
我使用 php-openssl 扩展,Python 我使用 ecdsa 0.13。但是,这两个都失败了。我再试一次,使用openssl命令,它也无法验证。
我不知道,我哪里错了。
为什么ECDSA验证失败?
这是我的代码:
生成签名(android):
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1"); try { KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA","SC"); g.initialize(spec, new SecureRandom()); KeyPair keyPair = g.generateKeyPair(); privateKey = keyPair.getPrivate(); publicKey = keyPair.getPublic(); ///write public key to pem file ...... FileOutputStream fileOutputStream1 = new FileOutputStream(file1); StringWriter writer1 = new StringWriter(); PemWriter pemWriter1 = new PemWriter(writer1); pemWriter1.writeObject(new PemObject("PUBLIC KEY",publicKey.getEncoded())); pemWriter1.flush(); pemWriter1.close(); String publickeyPem = writer1.toString(); fileOutputStream1.write(publickeyPem.getBytes()); fileOutputStream1.close(); } catch (Exception e) { e.printStackTrace();} ...... //Sign and veryfied String chuoi = txtChuoi.getText().toString(); byte[] chuoiInput = chuoi.getBytes("UTF-8"); Signature sig = Signature.getInstance("NONEwithECDSA","SC"); sig.initSign(privateKey); sig.update(chuoiInput); ///SIGN byte[] signatureBytes = sig.sign() txtMaHoa.setText(Base64.encodeToString(signatureBytes,Base64.DEFAULT)); sig.initVerify(publicKey); sig.update(chuoiInput); ///VERIFIED txtGiaiMa.setText(sig.verify(signatureBytes)+""); ///Write string sign in txtMahoa to file .......
我有签名和public密钥的输出是:
(Signature string) MEYCIQC7Hz631IFGsUOogcRLeN99uM9hWgLr+LGzuJvR/6nBrgIhAMXgZcvXyMRCAELXlNNS1a9j iAT1x0q2C5Mdu+2aZKtN
(公钥)
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEj07XEM+ulPyrdsfAf9prN2L2dNUd /Yy0rABcFdueAwYUf86f8Cc93Ws6sxzIvf2iKOapFby7EjHewjhLM/z7Qg== -----END PUBLIC KEY-----
使用PHP验证:
$pubkeyid = openssl_pkey_get_public("/var/www/html/ca.pem"); $data = "nguyen tran thanh Lam"; $signature = "MEYCIQC7Hz631IFGsUOogcRLeN99uM9hWgLr+LGzuJvR/6nBrgIhAMXgZcvXyMRCAELXlNNS1a9jiAT1x0q2C5Mdu+2aZKtN"; $ok = openssl_verify($data, $signature, $pubkeyid); if ($ok == 1) { echo "good"; } elseif ($ok == 0) { echo "bad"; } else { echo "ugly, error checking signature"; } ?>
Comment: ... but the verify function don't return anything. So I not sure
如果crypto.verify(...
没有抛出错误,这意味着验证成功。请参阅下面更新的代码。
使用错误的证书、签名或数据进行验证。
Comment: what do you think about add base64.b64decode. ... because in android code I have this
linetxtMaHoa.setText(Base64.encodeToString(signatureBytes,Base64.DEFAULT));
PEM
已经是base64
,检查上面的.setText(Base64...
是否可以省略。现在我已经用 base64.b64decode
.
Question: Example ---- b"sha256"? "b" is a binary (built-in funtion or I must convert string sha256 to binary?)
仅适用于我的字符串 (digest-names),例如:
import pem, base64
from OpenSSL import crypto
signature = b"MEYCIQC7Hz631IFGsUOogcRLeN99uM9hWgLr+LGzuJvR/6nBrgIhAMXgZcvXyMRCAELXlNNS1a9jiAT1x0q2C5Mdu+2aZKtN"
# As Creator of the Signatur do additional base64 encoding!
signature = base64.b64decode(signature)
data = "nguyen tran thanh Lam"
certificate = pem.parse_file('Android.pem')[0]
cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate.as_bytes())
try:
crypto.verify(cert=cert, signature=signature, data=data, digest='sha256')
except crypto.Error as exp:
print('crypto.Error:{}'.format(exp.args))
>>>crypto.Error:([('', 'ECDSA_do_verify', 'bad signature')],)
Note: Throws
crypto.Error
because not using thecertificate
which created thesignature
.
OpenSSL.crypto.verify(certificate, signature, data, digest)
Verify the signature for a data string.
certificate is a X509 instance corresponding to the private key which generated the signature.
signature is a str instance giving the signature itself.
data is a str instance giving the data to which the signature applies.
digest is a str instance naming the message digest type of the signature, for example b"sha256".New in version 0.11.