I have a server written in java that generates RSA key pair. I want to use the private key in a C++ client for decryption.
This is the code I use to create the private key:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(4096);
KeyPair keyPair = keyGen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
byte[] encoded = privateKey.getEncoded();
String b64Encoded = Base64.getEncoder().encodeToString(encoded);
I then save the base64 encoded string and try to load it as a private key in my cpp code.
I decode the base64 string to a binary array and then use the following code to try loading the private key (encodedString should be the DER encoded data after decoding it from base64):
ByteQueue queue;
StringSource ss(encodedString,true);
ss.TransferTo(queue);
queue.MessageEnd();
key.BERDecodePrivateKey(queue, false , queue.MaxRetrievable());
However this code always crashes with exception: CryptoPP::BERDecodeErr.
I believe both libraries use PKCS#8 to encode the key parameters.
Note: When I create the key pair using crypto++ and then encode it (DER + base64) I get a 3172/3176 chars string while in my java code I get a 3168 chars string. I'm not sure whether this info helps with anything.
It looks like you need to call Load and not BERDecodePrivateKey. You call Load when you have a key+info (like version and algorithm id); while you call BERDecodePrivateKey when you have just a key. The following works for me.
If the private key is password protected, then you will need the PEM Pack. The PEM Pack is a community contribution, but its maintained like the proper library.
$ cat rsa_java.java
import java.io.*;
import java.util.*;
import java.security.*;
public class rsa_java {
public static void main (String[] args) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(4096);
KeyPair keyPair = keyGen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
byte[] encodedPrivateKey = privateKey.getEncoded();
String b64Encoded = Base64.getEncoder().encodeToString(encodedPrivateKey);
try (PrintStream out = new PrintStream(new FileOutputStream("rsa_key.txt")))
{
out.print(b64Encoded);
}
}
And:
$ cat rsa_cryptopp.cxx
#include <iostream>
using namespace std;
#include "cryptopp/rsa.h"
#include "cryptopp/files.h"
#include "cryptopp/osrng.h"
#include "cryptopp/base64.h"
using namespace CryptoPP;
int main(int argc, char* argv[])
{
try
{
RSA::PrivateKey key;
FileSource fs("rsa_key.txt", true, new Base64Decoder);
key.Load(fs);
cout << "Loaded RSA key" << endl;
AutoSeededRandomPool prng;
key.Validate(prng, 3);
cout << "Validated RSA key" << endl;
}
catch(const Exception& ex)
{
cout << "Exception: " << ex.what() << endl;
}
return 0;
}
It results in:
$ javac rsa_java.java && java rsa_java
$ g++ -I. rsa_cryptopp.cxx cryptopp/libcryptopp.a -o rsa_cryptopp.exe
$ ./rsa_cryptopp.exe
Loaded RSA key
Validated RSA key
For completeness, here's the key with the Base64 encoding. Its 3168 bytes:
$ fold -w 80 -s rsa_key.txt
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
Related
I'm trying to decode/encode a signature with SHA256withECDSA.
I have a Java code that works fine:
public void verify() throws Exception {
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA"));
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode("your public key goes here"));
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update("All the webhook field values (only the values) concatenated together using a semicolon ;".getBytes("UTF-8"));
boolean result = ecdsaVerify.verify(Base64.getDecoder().decode("Signature to verify")); //Must return true
}
but I need this solution for nodejs.
when I'm creating an base64 buffer in nodejs it's getting a different results from the Java code.
I currently use cryptojs & jwa npm's.
Edit:
this is my code for nodejs:
var jwa = require("jwa");
const verifySignature = () => {
let public_key = Buffer.from("public key here", "base64");
let signature = Buffer.from("Signature_here", "base64");
let payload = "data here seperated by semicolon";
let ecdsa = jwa("ES256");
let verify = ecdsa.verify(payload, signature, public_key);
}
verifySignature();
JWA implements the algorithms used in JOSE, and in particular for ECDSA uses the signature format (aka encoding) defined in P1363 that simply contains fixed-length R and S concatenated; see steps 2 and 3 of https://datatracker.ietf.org/doc/html/rfc7518#section-3.4 . This is not the format used by most other standards, and in particular by Java, which is an ASN.1 DER-encoded SEQUENCE of two INTEGER values, which are (both) variable length (all) with tag and length prefixes.
You don't need JWA at all; builtin 'crypto' has the functionality you need:
import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class SO70655734 {
public static void main (String[] args) throws Exception {
String data = "some;test;data";
String pubkey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyOFORyCVCxX+W4QZevzH6skTbp2lVDtRLV0+7ypHjX26wFtSWSe4MTI0GZjIKDOAIT8KpbqH8HXI6Wo5S6/6hg==";
String sig = "MEQCICDLwztlKOXmQLuDJ0Hh96gGAT2/wsm2ymw3CDxSDnB3AiAK7eR8+C6g/zw5TmXUX0K/pV5kjIJTCieIkQXzH30WYA==";
Signature ecdsa = Signature.getInstance("SHA256withECDSA");
ecdsa.initVerify(KeyFactory.getInstance("EC").generatePublic(
new X509EncodedKeySpec(Base64.getDecoder().decode(pubkey)) ));
ecdsa.update(data.getBytes("UTF-8")); // or StandardCharsets.UTF_8
System.out.println(ecdsa.verify(Base64.getDecoder().decode(sig)));
}
}
-> true
const crypto = require('crypto');
const data = "some;test;data";
const pubkey = "-----BEGIN PUBLIC KEY-----\n"
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyOFORyCVCxX+W4QZevzH6skTbp2lVDtRLV0+7ypHjX26wFtSWSe4MTI0GZjIKDOAIT8KpbqH8HXI6Wo5S6/6hg==\n"
+ "-----END PUBLIC KEY-----\n";
const sig = "MEQCICDLwztlKOXmQLuDJ0Hh96gGAT2/wsm2ymw3CDxSDnB3AiAK7eR8+C6g/zw5TmXUX0K/pV5kjIJTCieIkQXzH30WYA==";
var ecdsa = crypto.createVerify('SHA256');
ecdsa.update(data,'utf8');
console.log( ecdsa.verify(pubkey, sig,'base64') );
-> true
Note: recent versions of nodejs can instead take DER-format pubkey (without the BEGIN/END lines added, but with the base64 converted to binary), but my most convenient test system only has 8.10.0 so I went the more compatible old way.
Adapting the directions at Creating a DSA Signature from the Linux command line I created a DSA signed message:
echo "foobar" > foo.txt
openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin
The directions actually used foo.sha1 instead of foo.txt, where foo.sha1 was generated by sha1sum but signing a hash seems a bit redundant since DSA is, itself, supposed to do hashing.
So, anyway, I did that. Here's the private key I used (I generated it specifically for testing purposes):
-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
ANi+lYG7xMKQ/bE4+bS8gemNAoGBAORowvirD7AB9x2SpdiME41+O4jVR8rs6+GX
Ml3Hif6Yt1kem0CeraX9SNoyBNAzjD5TVMGIdGlgRr6GNreHeXMGWlvdDkvCACER
ZEEtMsKZicm+yl6kR8AGHTCA/PBltHfyrFQd4n9I//UDqI4RjqzvpCXGQcVEsSDY
CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----
Here's the hex encoded output of sigfile.bin:
302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5
I'm now trying to verify this in Java with BouncyCastle and am unable to do so. Here's my Java code:
import java.io.StringReader;
import org.bouncycastle.openssl.PEMReader;
import java.security.interfaces.DSAPublicKey;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.DSAParameters;
public class DSA
{
public static void main(String[] args)
throws Exception
{
byte[] message = "foobar".getBytes();
byte[] signature = hexStringToByteArray("302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5");
String key = "-----BEGIN PUBLIC KEY-----\n" +
"MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI\n" +
"uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6\n" +
"lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb\n" +
"5SmwZ+UVx3zfAhUAu0kA2L6VgbvEwpD9sTj5tLyB6Y0CgYEA5GjC+KsPsAH3HZKl\n" +
"2IwTjX47iNVHyuzr4ZcyXceJ/pi3WR6bQJ6tpf1I2jIE0DOMPlNUwYh0aWBGvoY2\n" +
"t4d5cwZaW90OS8IAIRFkQS0ywpmJyb7KXqRHwAYdMID88GW0d/KsVB3if0j/9QOo\n" +
"jhGOrO+kJcZBxUSxINgIIEYFAlEDgYUAAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX4\n" +
"3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg\n" +
"zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M\n" +
"TzUkQjFI9UY7kZeK\n" +
"-----END PUBLIC KEY-----";
PEMReader reader = new PEMReader(new StringReader(key));
DSAPublicKey decoded = (DSAPublicKey) reader.readObject();
DSADigestSigner dsa = new DSADigestSigner(new DSASigner(), new SHA1Digest());
DSAParameters params = new DSAParameters(
decoded.getParams().getP(),
decoded.getParams().getQ(),
decoded.getParams().getG()
);
DSAPublicKeyParameters publickey = new DSAPublicKeyParameters(decoded.getY(), params);
dsa.init(false, publickey);
dsa.update(message, 0, message.length);
boolean result = dsa.verifySignature(signature);
System.out.println(result ? "good" : "bad");
}
public static byte[] hexStringToByteArray(String s)
{
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
The signature is not validating. Is there something wrong with my Java code? Maybe OpenSSL is doing something weird with dss1?
I was able to validate the signature just fine with OpenSSL:
openssl dgst -dss1 -verify dsa_pub.pem -signature sigfile.bin foo.txt
(Unix) echo outputs its arguments, space-separated if more than one, PLUS A NEWLINE. Use "foobar\n" as the data to verify. Alternatively sign the result of printf '%s' foobar >foo.txt which portably omits the newline; some versions of echo support -n for this purpose, some older ones use \c, and some don't support it at all.
FYI BouncyCastle as of version 150 (2013) no longer has org.bouncycastle.openssl.PEMReader; instead you need PEMParser which returns org.bouncycastle.asn1.x509.SubjectPublicKeyInfo which can be converted to key object by org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter or KeyFactory.getInstance(alg).generatePublicKey(new X509EncodedKey(spki.getEncoded())) which is what JcaPEMKeyConverter actually does.
OTOH you can use org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil.generatePublicKeyParameter to replace that fiddling with the parameter pieces; that's what the BC provider interface (as opposed to the lightweight interface) does. Or of course you could just use JCA in the first place and you don't really need BC at all, since OpenSSL publickey formats (unlike privatekey) are consistently compatible with basic Java crypto.
Also BTW openssl dgst needed the -dss1 hack only through version 0.9.8; since version 1.0.0 released 2010 (but not immediately upgraded by many distros and products due to actual or feared incompatibility) you only need -sha1 and a DSA pubkey.
I have to implement a RC4 cipher in NodeJS, here is the code:
function cipher (CRYPTO, str) {
const cipher = crypto.createCipher(CRYPTO.cipherAlgorithm, CRYPTO.password);
return Buffer.concat([
cipher.update(str, 'utf-8'),
cipher.final()
]).toString(CRYPTO.encoding);
}
const CRYPTO = {
cipherAlgorithm: 'rc4',
password: 'trololol',
encoding: 'base64'
};
cipher(CRYPTO, '0612345678');
// returns 'yTXp/PZzn+wYsQ=='
When i check my implementation with open ssl, i've got the same result:
echo -ne "0612345678" | openssl rc4 -pass "pass:trololol" -e -nosalt | base64
> yTXp/PZzn+wYsQ==
But with our partner implementation, the result is really different. It is written in Java so i tried to do one and i have the same result than him:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class Encryptor {
private static String algorithm = "RC4";
public static String encrypt(String key, String value) {
try {
SecretKeySpec rc4Key = new SecretKeySpec(key.getBytes(), algorithm);
Cipher rc4 = Cipher.getInstance(algorithm);
rc4.init(Cipher.ENCRYPT_MODE, rc4Key);
byte [] encrypted = rc4.update(value.getBytes());
return DatatypeConverter.printBase64Binary(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String key = "trololol";
String value = "0612345678";
System.out.println(encrypt(key, value));
}
}
Running the above gives:
javac Encryptor.java && java Encryptor
> LYlbWr0URiz+wA==
Is it possible that the RC4 algorithm in Java differs from the other ones or is there something wrong in the Java implementation?
The difference is "password" vs "key."
For example with node and OpenSSL, "password" means some value to hash (using MD5) to generate the key for encryption/decryption.
If you instead use the "password" value as the key (with an empty IV), you will match the value received from Java. For example with node, change to the createCipheriv() function:
crypto.createCipheriv(CRYPTO.cipherAlgorithm, CRYPTO.password, Buffer.alloc(0));
I have use case where i want to to encrypt some data using private key in C and decrypt it using public key in java.
I generated the the public/private key using openssl. I am able to run a c code for encrypting the data using private key.
Something like following code :
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/err.h>
int padding = RSA_PKCS1_PADDING;
char * data;
char *encrypted;
FILE * fp = fopen(<private_key_file>,"rb");
RSA *rsa= RSA_new() ;
rsa = PEM_read_RSAPrivateKey(fp, &rsa,NULL, NULL);
RSA_private_encrypt(data_len,data,encrypted,rsa,padding);
This works fine and i am also able to decrypt it using public key in C. I am not able to decrypt the same using public key in Java. For this, I converted the public key to DER format :
$ openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
I am using following code
public String decrypt(byte[] encrypted) throws Exception
{
Cipher cipher = Cipher.getInstance("RSA");
KeyFactory kf = KeyFactory.getInstance("RSA");
byte[] encKey = readFromFile(PUBLIC_KEY_FILE, false);
X509EncodedKeySpec ks = new X509EncodedKeySpec(encKey);
PublicKey pk = kf.generatePublic(ks);
cipher.init(Cipher.DECRYPT_MODE, pk);
byte[] plainText = cipher.doFinal(encrypted);
return new String(plainText,"UTF-8");
}
I get the following exception with my java code.
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
at AssyncKeyEncryptTest.decrypt(AssyncKeyEncryptTest.java:198)
at AssyncKeyEncryptTest.test(AssyncKeyEncryptTest.java:45)
at AssyncKeyEncryptTest.main(AssyncKeyEncryptTest.java:32)
Can someone please help me in fixing the error in decrypting the data using public key in java ?
The stacktrace suggests different padding between encryption and decryption.
Try "PKCS1Padding" as instance for decryption as you used it while encrypting.
Try "RSA/None/PKCS1Padding" instead of "RSA".
If you are unable to find a Cipher instance with "RSA/None/PKCS1Padding" then try adding a security provider like Bouncy Castle Provider.
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Adding the bouncy castle dependency should be straightforward enough.
http://www.bouncycastle.org/latest_releases.html
For my current project I have to send a signature from PHP to Java application. I am using Crypt/RSA right now for signing my data.
For test I am signing just "abc" with following code :
$rsa = new Crypt_RSA();
$plaintext = 'abc';
$rsa->loadKey("MIICXgIBAAKBgQDjh+hNsqJe566JO0Sg7Iq5H1AdkauACdd8QMLp9YNY0HPslVH0
rXaOFo0zgH0Ktu/Ku3lS1lfxbFQAY8b6ywZKvu4eoxlnEwuBwy09CG+3ZiVLBjCj
TZHA/KOkpVLa+tA6KsoP6zv/xI/ACkSCxPGR0q3SiRuhXV/6tacoKxUYnwIDAQAB
AoGBAIC00GOjONYWmFRoglnFdHNjkx4m2KyE5LAUsi1GBBapU+nwTXvq47VcbGNF
u3XkJaC4i9igBv86GApgZp5XWia86On/Lz9NR4fB2EFP6Ydy84GfCDNNvkism4BR
aA+eYdNiQ3Wfyi98ZpUi+rPsoI6Cid4eSkCC4poTUaqzMkiBAkEA9Gn1oIlUEoVI
q/u5Y9vflXRDt95AA9AokJkQj7XTNjsz8ypU8TO6D6ZykpcbK6zjU0UJsQiC3dKj
AgmAR2VzYwJBAO5RETMAyDnR+5g+MtHpwGqGdY4dq0j4y4CsdtOYKWwSTh3VQy+C
eghJoyPRfIpulw2Mk/l+occEI0ohJl0+UJUCQQDSZtjVLwMZwnUx4EvSw/ewL9sP
0Jpo7evNtoaEQDEncUWiYeGnljDowg/FU6FHMtiq2TajmMEXdflvioBMdfAjAkEA
3TB60SbJr/i4Fo6sJm5ZO8W+eAALiTf50VzBERTqZTb8L+5PZFoqn2SROV5mxClu
o5G1idzBlHC/vD7WV7bNnQJAd0FrxaMBurJ4Uv/B8TDP+eeBdB7d9rKw0+TVlcel
cbpIz6BIP6+nmsgy6dbDRnx0eC/MgF2EU0wrCu1DK0PyWA==");
$rsa->setHash("sha256");
$signature = $rsa->sign($plaintext);
$signature_encoding = mb_convert_encoding($signature, "UTF-8");
error_log("signature encoded in UTF-8 :" . $signature_encoding);
$encoded_sign = base64_encode($signature_encoding);
error_log("encoded sign for abc: " . $encoded_sign);
I can verify the signature from php code. But when it comes to verifying from JAVA, i was not successfull. Here is the java code that does the verify operation :
public boolean verify(String signed, String data, PubKey pubKey) throws Exception{
PublicKey publicKey = jceProvider.generateRSAPublicKeyFromX509(
base64.decode(pubKey.getEncodedKey())
);
byte[] signature = base64.decode(signed);
byte[] verifier = data.getBytes(Charset.forName("UTF-8"));
return jceProvider.verify(signature, verifier, publicKey);
}
public class JCEProvider {
public boolean verify (byte[] signature, byte[] verifier, PublicKey publicKey) throws Exception{
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
rsaSignature.initVerify(publicKey);
rsaSignature.update(verifier);
return rsaSignature.verify(signature);
}
I dont think it is because of keys, I can already verify it from PHP as I told before. There is something that I miss about PHP encoding or byte streams but I am lost for the moment.
Any help would be appreciated.
I'm using openssl like Whity already mentioned. Here is my striped down example. Be aware of any character encoding, line ending, etc. This results in changed binary representation of your text data.
PHP-RSA_SHA256-Sign:
<?php
$data = "For my current project I have to send a signature from PHP to Java application. I am using Crypt/RSA right now for signing my data.";
$private_key = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6zxqlVzz0wy2j4kQVUC4Z
RZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQJAL151ZeMKHEU2c1qdRKS9
sTxCcc2pVwoAGVzRccNX16tfmCf8FjxuM3WmLdsPxYoHrwb1LFNxiNk1MXrxjH3R
6QIhAPB7edmcjH4bhMaJBztcbNE1VRCEi/bisAwiPPMq9/2nAiEA3lyc5+f6DEIJ
h1y6BWkdVULDSM+jpi1XiV/DevxuijMCIQCAEPGqHsF+4v7Jj+3HAgh9PU6otj2n
Y79nJtCYmvhoHwIgNDePaS4inApN7omp7WdXyhPZhBmulnGDYvEoGJN66d0CIHra
I2SvDkQ5CmrzkW5qPaE2oO7BSqAhRZxiYpZFb5CI
-----END RSA PRIVATE KEY-----
EOD;
$binary_signature = "";
$algo = "SHA256";
openssl_sign($data, $binary_signature, $private_key, $algo);
print(base64_encode($binary_signature) ."\n");
?>
The output of base64 encoded binary signature is:
OnqiWnFQ2nAjOa1S57Du9jDpVr4Wp2nLdMk2FX+/qX1+SAHpVsW1JvQYqQUDlxvbTOE9vg6dlU6i3omR7KipLw==
JAVA-RSA_SHA256-Verify:
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import org.apache.commons.codec.binary.Base64;
public class RsaVerify {
public static void main(String args[]){
String publicKey =
// "-----BEGIN PUBLIC KEY-----"+
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6"+
"zxqlVzz0wy2j4kQVUC4ZRZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQ==";
// "-----END PUBLIC KEY-----";
byte[] data = "For my current project I have to send a signature from PHP to Java application. I am using Crypt/RSA right now for signing my data.".getBytes();
byte[] signature = Base64.decodeBase64("OnqiWnFQ2nAjOa1S57Du9jDpVr4Wp2nLdMk2FX+/qX1+SAHpVsW1JvQYqQUDlxvbTOE9vg6dlU6i3omR7KipLw==");
try {
System.out.println(verify(data, signature, publicKey));
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
}
private static boolean verify(byte[] data, byte[] signature, String publicKey) throws GeneralSecurityException{
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
sig.update(data);
return sig.verify(signature);
}
}
phpseclib uses the more secure PSS padding by default. Java is probably using PKCS#1 padding. So if you were to go the phpseclib route (which I'd recommend doing)... do this:
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
I think u need to improve your PHP solution.
According to http://php.net/manual/en/function.openssl-get-md-methods.php you can use directly [47] => sha256WithRSAEncryption from PHP, probably call openssl from commandline also be possible:
openssl dgst -sha256 -sign my.key -out in.txt.sha256 in.txt