【发布时间】:2026-02-16 08:40:02
【问题描述】:
我们使用 phpseclib 对数据进行公钥签名,而 android java 用于公钥验证。但它一再失败。
生成密钥并通过私钥签名的PHP代码
include_once("phpseclib/autoload.php");
function getKeys($keysize=2048){
$rsa = new Crypt_RSA();
//$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
//$rsa->setPublicKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
return array("publickey"=>$d['publickey'], "privatekey"=>$d['privatekey']);
}
function encryptdata($message, $encryptionKey){
$rsa = new Crypt_RSA();
//$rsa->setPublicKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
//$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
$rsa->loadKey($encryptionKey); // public key
return $rsa->encrypt($message);
}
function decryptdata($message, $decryptionKey){
$rsa = new Crypt_RSA();
// $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
// $rsa->setPublicKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$rsa->loadKey($decryptionKey); // private key
return $rsa->decrypt($message);
}
$keys = getKeys();
file_put_contents("key.pub", $keys["publickey"]);
file_put_contents("key.priv", $keys["privatekey"]);
$publickey = file_get_contents("key.pub");
$privatekey = file_get_contents("key.priv");
//print_r($keys);
$string = "Hi I m here";
$hash = hash("sha256", $string);
$encdata = encryptdata($hash, $privatekey);
echo $base_encdata = base64_encode($encdata);
JAVA代码
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.lang.String;
class PubCheck {
public static boolean verify(String message, String signature, PublicKey publicKey) throws SignatureException{
try {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
} catch (Exception ex) {
throw new SignatureException(ex);
}
}
public static void main(String[] args)
throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException, SignatureException
{
String plainData = "Hi I m here";
String pkey = "MIIBCgKCAQEA2tF2g/muNw9xKTVcIkjUMvMhygtIW49yo1PgbwqDQ/w9MSfEARtYYF6Tenfz0twaR/eI14GXmlIffflORe4eaSuMBhwQFOIKU/1+v1BV3RLqGGblvHTVaMVm49AGiqxNnh1LBbcSrC5UhMqlL/HGiku0oYsbjLzwcLc5ac6aBQVD60wWGNm1g26lRQGRbCLqxVfcWKT3AMvEQK3cEx/En7/5Vg1V8xnJraNMrO8UGnaX8LLJFzYJiSCEShh7F+pMHbf4MaBekw7Aaf5hPJtczNsR137R92Be3OP4idI5NLmTV+Pi1DWlxhjEhswKH88SP+gsW31gS7B/ddECUqewQwIDAQAB";
String data = "aP0nuYYA1hE5odsCkR/DcdRbBvO2Z8IOlqXf/bKZJiG8HELIop90Vno1dKC1qyHEAOXy0gtH7GtJamzoBjDZmHPT6eto9EZP/xE7xZ8L05kjp0z2thLqO7on4C6DrG++TK1j+E3T7V0UeU874WIB0AEVzu1XUKFW6aeuU67a/gdn8N2n7N/WXtlyNSVZXg8f4PeUhGvFJrhINZT7BuMMZj1gZs4wMJPAICwfvVeg02RPH0N3Ybf2iVgRuZlmtQXGTyBlCxe9ybdHzuQM6nXghpLNmaOzCypb+yVs3Da7E0b3/fKQ7JqPSquWex2ERZbIMSTC6oCzc1rOF6iKVAd92Q==";
byte[] encodedPublicKey = pkey.getBytes( "utf-8" );
//System.out.println(new String(encodedPublicKey, "UTF-8") + "\n");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( encodedPublicKey );
//PKCS8EncodedKeySpec publicKeySpec = new PKCS8EncodedKeySpec(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance( "RSA" );
PublicKey publicKey = keyFactory.generatePublic( publicKeySpec );
boolean retvar = verify(plainData, data, publicKey);
// 3 - verifying content with signature and content :
/*Signature sig = Signature.getInstance( "SHA256withRSA" );
sig.initVerify( publicKey );
sig.update( data.getBytes( ) );
ret = sig.verify( sign.getBytes( ) );*/
//byte[] decoded = Base64.decodeBase64(data);
}
}
我通过
编译了java代码javac -cp commons-codec-1.10.jar:. PubCheck.java
java -cp commons-codec-1.10.jar:. PubCheck
然后发现以下异常
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at PubCheck.main(PubCheck.java:67)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
at sun.security.x509.X509Key.decode(X509Key.java:403)
at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:83)
at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:298)
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:201)
... 2 more
免责声明:我对 java 的了解为零。我尝试从网上找到的所有代码。
更新:问题终于解决了,Java 代码能够在 Maarten Bodewes 的帮助下进行验证。他提供的代码适用于我需要从 phpseclib 传递 PKCS1 的一项更改所以我更改了
Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
到
Signature sig = Signature.getInstance( "SHA256withRSA");
PHP 代码需要更改以使用符号而不是手动加密/散列。
function getKeys($keysize=2048){
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
return array("publickey"=>$d['publickey'], "privatekey"=>$d['privatekey']);
}
$string = "Hi I m here";
/*
$keys = getKeys();
file_put_contents("key1.pub", $keys["publickey"]);
file_put_contents("key1.priv", $keys["privatekey"]);
die;*/
$publickey = file_get_contents("key1.pub");
$privatekey = file_get_contents("key1.priv");
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($privatekey);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($string);
echo base64_encode($signature);
【问题讨论】:
-
仅仅从互联网上复制代码永远不会为您提供有效的解决方案,如果这样做,则有 90% 的可能性是不安全的(例如,使用 OAEP 验证签名)。跨度>
-
@MaartenBodewes,Java 实现将由专门的 android 程序员完成,但他因为错误而无法读取密钥而被卡住(他在 java 加密方面没有太多经验),所以我尝试了java代码来模拟和调试错误以帮助他。显然我们会选择更好的 java 安全模型,但现在的首要目标是从 php 服务读取和验证密钥。
-
好的,如果我的解决方案有效,请告诉我 - 请不要忘记跟进。
-
作者:正确的做法是永远不要使用 PKCS1,使用 OAEP 进行加密,使用 PSS 进行签名。使用 OAEP 进行签名很奇怪;不要那样做。另外,请确保您使用的是 e=65537。
标签: java php cryptography digital-signature phpseclib