Как RSA проверить подпись в java, которая была сгенерирована в php

Мы используем 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. Код, который он предоставил, работает с одним изменением. Мне нужно передать PKCS1 из phpseclib. Итак, я изменил

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

в

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

PHP Code нуждается в изменениях для использования знака вместо ручного шифрования / хэширования.

 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); 

Solutions Collecting From Web of "Как RSA проверить подпись в java, которая была сгенерирована в php"

Клавиши PKCS # 1 почти, но не полностью совпадают с ключами X.509.

Следующий фрагмент создаст открытый Java JCA-ключ. Затем он попытается выполнить дешифрование (по умолчанию) OAEP.

 package nl.owlstead.stackoverflow; import static java.nio.charset.StandardCharsets.UTF_8; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.security.Signature; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; import java.util.Base64; import java.util.Base64.Decoder; import javax.crypto.Cipher; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class PKCS1PublicKey { public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) { // --- parse public key --- org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey; try { pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey .getInstance(pkcs1EncodedPublicKey); } catch (Exception e) { throw new IllegalArgumentException( "Could not parse BER PKCS#1 public key structure", e); } // --- convert to JCE RSAPublicKey RSAPublicKeySpec spec = new RSAPublicKeySpec( pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent()); KeyFactory rsaKeyFact; try { rsaKeyFact = KeyFactory.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("RSA KeyFactory should be available", e); } try { return (RSAPublicKey) rsaKeyFact.generatePublic(spec); } catch (InvalidKeySpecException e) { throw new IllegalArgumentException( "Invalid RSA public key, modulus and/or exponent invalid", e); } } public static void main(String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider()); String pkey = "MIIBCgKCAQEA2tF2g/muNw9xKTVcIkjUMvMhygtIW49yo1PgbwqDQ/w9MSfEARtYYF6Tenfz0twaR/eI14GXmlIffflORe4eaSuMBhwQFOIKU/1+v1BV3RLqGGblvHTVaMVm49AGiqxNnh1LBbcSrC5UhMqlL/HGiku0oYsbjLzwcLc5ac6aBQVD60wWGNm1g26lRQGRbCLqxVfcWKT3AMvEQK3cEx/En7/5Vg1V8xnJraNMrO8UGnaX8LLJFzYJiSCEShh7F+pMHbf4MaBekw7Aaf5hPJtczNsR137R92Be3OP4idI5NLmTV+Pi1DWlxhjEhswKH88SP+gsW31gS7B/ddECUqewQwIDAQAB"; Decoder decoder = Base64.getDecoder(); byte[] dpkey = decoder.decode(pkey); RSAPublicKey publicKey = fromPKCS1Encoding(dpkey); String plainData = "Hi I m here"; String data = "aP0nuYYA1hE5odsCkR/DcdRbBvO2Z8IOlqXf/bKZJiG8HELIop90Vno1dKC1qyHEAOXy0gtH7GtJamzoBjDZmHPT6eto9EZP/xE7xZ8L05kjp0z2thLqO7on4C6DrG++TK1j+E3T7V0UeU874WIB0AEVzu1XUKFW6aeuU67a/gdn8N2n7N/WXtlyNSVZXg8f4PeUhGvFJrhINZT7BuMMZj1gZs4wMJPAICwfvVeg02RPH0N3Ybf2iVgRuZlmtQXGTyBlCxe9ybdHzuQM6nXghpLNmaOzCypb+yVs3Da7E0b3/fKQ7JqPSquWex2ERZbIMSTC6oCzc1rOF6iKVAd92Q=="; byte[] ciphertext = decoder.decode(data); // this will fail of course if the "signature" was generated using OAEP - use PSS signatures instead (see comments below) verifyBC(publicKey, plainData, ciphertext); System.out.flush(); decryptBC(publicKey, plainData, ciphertext); System.out.flush(); decryptSun(publicKey, plainData, ciphertext); System.out.flush(); } private static void decryptBC(RSAPublicKey publicKey, String plainData, byte[] ciphertext) throws Exception { Cipher oaep = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); // this *should* fail oaep.init(Cipher.DECRYPT_MODE, publicKey); byte[] plaintext = oaep.doFinal(ciphertext); System.out.println(new String(plaintext, UTF_8)); } private static void decryptSun(RSAPublicKey publicKey, String plainData, byte[] ciphertext) throws Exception { Cipher oaep = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "SunJCE"); // this fails beautifully oaep.init(Cipher.DECRYPT_MODE, publicKey); byte[] plaintext = oaep.doFinal(ciphertext); System.out.println(new String(plaintext, UTF_8)); } private static void verifyBC(RSAPublicKey publicKey, String plainData, byte[] ciphertext) throws Exception { // what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider Signature sig = Signature.getInstance( "SHA256withRSAandMGF1"); sig.initVerify(publicKey); sig.update(plainData.getBytes(UTF_8)); System.out.println(sig.verify(ciphertext)); } } 

Реализация OAEP SunJCE завершится неудачно, поскольку он не примет открытый ключ для проверки подписи:

OAEP не может использоваться для подписи или проверки подписей

Теперь это должно быть одним из самых ясных и информативных исключений, которые я встречал в API криптографии. Вы также можете использовать провайдера Bouncy Castle, и он будет «расшифровывать» значение хэш-функции. Однако не следует использовать OAEP, вы должны использовать PSS для проверки подписей.

Вместо этого вы должны использовать метод sign RSA, используя setHash для настройки SHA-256.

Хотя ответ Мартина работает, есть еще один способ избавиться от исключения InvalidKeySpecException.

В исходном коде pkey представляет собой закрытый ключ RSA в формате PKCS1. Это должен быть закрытый закрытый ключ PKCS8 для работы с X509EncodedKeySpec (который соответствует объекту SubjectPublicKeyInfo сертификата X509). Он также должен быть декодирован base64.

Поэтому в вашем PHP-коде вы не будете делать $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1) – вы бы сделали $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS8) .

Я сам преобразовал ключ PKCS1 в PKCS8 и получил следующее:

 String pkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2tF2g/muNw9xKTVcIkjU" + "MvMhygtIW49yo1PgbwqDQ/w9MSfEARtYYF6Tenfz0twaR/eI14GXmlIffflORe4e" + "aSuMBhwQFOIKU/1+v1BV3RLqGGblvHTVaMVm49AGiqxNnh1LBbcSrC5UhMqlL/HG" + "iku0oYsbjLzwcLc5ac6aBQVD60wWGNm1g26lRQGRbCLqxVfcWKT3AMvEQK3cEx/E" + "n7/5Vg1V8xnJraNMrO8UGnaX8LLJFzYJiSCEShh7F+pMHbf4MaBekw7Aaf5hPJtc" + "zNsR137R92Be3OP4idI5NLmTV+Pi1DWlxhjEhswKH88SP+gsW31gS7B/ddECUqew" + "QwIDAQAB"; byte[] encodedPublicKey = Base64.decodeBase64(pkey); 

Вам, конечно же, нужно будет удалить существующие pkey и encodedPublicKey.

Кроме того, вы могли бы return $d вместо return array("publickey"=>$d['publickey'], "privatekey"=>$d['privatekey']) в вашем PHP-коде ..