Android verify signature always returns false - java

I'm trying to verify some signed content using ECDSA and spongycastle. Here is the code I'm using to generate the keypair with the brainpoolP512t1 ec curve:
public static KeyPair getKeyPairbrainpoolP512t1() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "SC");
ECNamedCurveParameterSpec curveParameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolP512t1");
keyPairGenerator.initialize(curveParameterSpec, new SecureRandom());
return keyPairGenerator.generateKeyPair();
}
Here is the code I'm using to sign and verify signatures:
private byte[] ecdsaSign(ECPrivateKey key, byte[] content) throws Exception {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA", "SC");
ecdsaSign.initSign(key);
ecdsaSign.update(content);
byte[] signature = ecdsaSign.sign();
return signature;
}
public static boolean ecdsaVerify(ECPublicKey key, byte[] content, byte[] signature) throws Exception {
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", "SC");
ecdsaVerify.initVerify(key);
ecdsaVerify.update(content);
boolean result = ecdsaVerify.verify(signature);
return result;
}
I'm passing in the bytes of a simple string message that was signed using the private key, and also the public key in order to verify. I'm always getting false however. What am I doing wrong? Any help is deeply appreciated.

Figured out what was wrong. I was exporting the keys to PEM in order to have them in string format prior to calling sign and verify, and then decoding back to private key original format. When I omitted this conversion and called Verify directly with the private key (without the PEM string back and forth conversion), the content got verified.

Related

How to Hash a given data using clients digital signature in java SHA 256 Algorithm

We want to Hash a data using clients digital signature using java sha 256 bit hashing algorithm.
How can we add digital signature while hashing in java.
If I'm understanding correctly, you want to sign some data. Here is a sample method:
public static String encode(String dataToEncode, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
byte[] decodedSecret = Base64.getDecoder().decode(secret);
SecretKeySpec keySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
Mac sha256 = Mac.getInstance("HmacSHA256");
sha256.init(keySpec);
return Base64.getEncoder().encodeToString(sha256.doFinal(dataToEncode.getBytes()));
}
The secret is the Base64 encoded secret key. The method returns the Base64 encoded hash of the data. The Base64 part is optional, you can remove it if you don't need that encoding. This is a method I use when signing REST API calls to crypto exchanges.
The following solution signs a String by applying an RSA PKCS#8 formatted private key. If your code has read the private key as a text from a pem file that looks like the following example:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRxFWXGYDG8zKw
ihIS+Ydh/nWX9NwkFTKMRjH8BQ78ZEnXrnGJHvd+dI+zEiRo7rLuDXMOjsnhIR/O
....
+wqssDAApq+CiPcBnn0x2Vw=
-----END PRIVATE KEY-----
Then you need to strip out the first and last lines and all the new line characters ('\n'). If your privateKey is read (from a java keystore for example) you can remove the lines of code that convert the String of private key into java.security.PrivateKey object.
private static String signSHA256RSA(String inputStr, String inputKey) throws Exception {
String key = inputKey.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] keyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(inputStr.getBytes("UTF-8"));
byte[] s = signature.sign();
return Base64.getEncoder().encodeToString(s);
}

I had generate Public Key by Java spring security and I try to use that public key to encrypt data with Nodejs Crypto but it return error

I had generate public key using Java Spring Security, but I can not use that public key to encrypt the data using Nodejs crypto library. I think it is because of its format(X509).
My Nodejs code
module.exports.encryptRsa = (toEncrypt, pemPath) => {
let absolutePath = path.resolve(pemPath);
let publicKey = fs.readFileSync(absolutePath, "utf8");
let buffer = Buffer.from(toEncrypt);
let encrypted = crypto.publicEncrypt(publicKey, buffer);
return encrypted.toString("base64");
};
My Java code
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(keyAlgorithm);
keyGen.initialize(2048);
KeyPair keyPair = keyGen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();
String formatPrivate = privateKey.getFormat(); // PKCS#8
String formatPublic = publicKey.getFormat(); // X.509
FileWriter fos = new FileWriter("publicKey.pem");
fos.write("-----BEGIN RSA PUBLIC KEY-----\n");
fos.write(enc.encodeToString(publicKeyBytes));
fos.write("\n-----END RSA PUBLIC KEY-----\n");
fos.close();
Java's getEncoded() method returns the public key in format called 'spki' by Node crypto. Java's name for that format is "X.509", an unfortunate choice because it causes confusion with certificates of that name.
The proper PEM header for spki keys is simply -----BEGIN PUBLIC KEY-----. Just get rid of RSA in the header and footer.

Cannot verify rsa signature on Android

I'm trying to sign an encrypted message with a private key and verify it in Java. This is my first time working with encryption and signatures so I'm not sure how it is supposed to work and I'm kind of stuck here. The verification always returns false.
Here I sign the message:
public byte[] rsaSign (byte[] data) {
byte[] cipherData = null;
try {
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(signModulus, signExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(keySpec);
Signature s = Signature.getInstance("SHA1withRSA");
s.initSign(privKey);
s.update(data);
return s.sign();
}
return cipherData;
}
And here I try to verify the signature:
public boolean rsaVerify (byte[] data, byte[] signature) {
boolean success = false;
try {
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(signModulus, signPublicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);
Signature s = Signature.getInstance("SHA1withRSA");
s.initVerify(pubKey);
s.update(data);
success = s.verify(signature);
return success;
}
return false;
}
Can anyone see a problem? The keys are generated in C# and converted to BigIntegers in java.
Signature verification is failed because you are using a different public key in the verification method.
Use the public key to verify the signature which is consistent with the private key that is used into rsaSign() method.
Hope this will help you. Note that, this public key is consistent with the private key which is used in Signature Generation method :
/**
* This method will sign message with RSA 2048 key
* #return Void
*/
public void rsaSign (String message) throws Exception {
//key generation
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(2048, random);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey priv = keyPair.getPrivate();
PublicKey pub = keyPair.getPublic();
System.out.println("RSAPub key Mod for Sign/Verify : " + Helper.toHex(((RSAPublicKey)pub).getModulus().toByteArray()));
System.out.println("RSAPub key Exp for Sign/Verify : " + Helper.toHex(((RSAPublicKey)pub).getPublicExponent().toByteArray()));
//sign
Signature dsa = Signature.getInstance(signALG);
dsa.initSign(priv);
dsa.update(Helper.toByte(message));
byte[] realSig = dsa.sign();
System.out.println("RSA Sign-Data : " + Helper.toHex(realSig));
}
/**
* This method verify signature with RSA public key
* #param message The plain message
* #param rsaMOD RSA Public key Modulus in string
* #param rsaEXP RSA Public key Exponent in string
* #param rsaSignData Signature which will be verified
* #return true if verifications success, false otherwise
*/
public boolean rsaVerify(String message, String rsaMOD, String rsaEXP, String rsaSignData) throws Exception {
BigInteger modBigInteger = new BigInteger(Helper.toByte(rsaMOD));
BigInteger exBigInteger = new BigInteger(Helper.toByte(rsaEXP));
RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(spec);
Signature signature = Signature.getInstance(signALG);
signature.initVerify(publicKey);
signature.update(Helper.toByte(message));
return signature.verify(Helper.toByte(rsaSignData));
}
You should try and test these things locally first, with your own generated key pair. If that fails your code is wrong - it's a very simple wrapper around Java Signature so that's not at all that likely.
You already used a complete specification of the signature algorithm, so provider defaults are not an issue here.
Then check the correctness of the data on both sides by printing it out in Hex or Base64 right before signature generation/verification. If that fails you've got an I/O or encoding/decoding error. Encoding/decoding errors & string handling make up about 30% of the total of cryptography related questions!
Finally you could obtain and compare the modulus of the private and public key. If the moduli don't match then you're using a private and public key of a different key pair, and signature verification will of course always fail.

SHA256withRSA sign from PHP verify from JAVA

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

javax.crypto.BadPaddingException: Data must start with zero” exception. Why does it occur? [duplicate]

I encountered the abovementioned exception while I was decrypting a string.
Below is my code:
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
public class EncryptAndDecrypt {
public static Cipher createCipher () throws Exception{
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
return cipher;
}
public static KeyPair generateKey () throws NoSuchAlgorithmException{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance ("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
return key;
}
public static byte [] encrypt (String str, Cipher cip, KeyPair key) {
byte [] cipherText = null;
try {
byte [] plainText = str.getBytes("UTF8");
cip.init(Cipher.ENCRYPT_MODE, key.getPublic());
cipherText = cip.doFinal(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return cipherText;
}
public static String decrypt (byte [] c, Cipher cip, KeyPair key) throws Exception {
cip.init(Cipher.DECRYPT_MODE, key.getPrivate());
byte [] decryptedPlainText = cip.doFinal (c);// exception occurred here
String decryptedPlainStr = new String (decryptedPlainText);
return decryptedPlainStr;
}
}
//separate class below to use the encrypt method
public class EncryptionApp {
public static void main (String [] args) {
getEncrypted();
}
public static byte [] getEncrypted () {
byte [] encyptedByte = null;
try {
String plainText = "der";
Cipher cip = Safety.createCipher();
KeyPair key = Safety.generateKey();
encyptedByte = Safety.useRSA(plainText, cip, key);
}
catch (Exception e) {
e.printStackTrace();
}
return encyptedByte;
}
}
// Another class to use the decrypt method
public class DecryptionApp {
public static void main(String[] args) {
System.out.println (useDecrypted () );
}
public static byte[] useDecrypted () {
byte [] decryptedText = null;
try {
Cipher cip = EncryptAndDecrypt.createCipher();
KeyPair key = EncryptAndDecrypt.generateKey();
decryptedText = EncryptAndDecrypt.decrypt(EncryptionApp.getEncrypted(),cip,key);
}
catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
}
You already asked the same question in "javax.crypto.BadPaddingException: Data must start with zero" exception, and I gave you an answer: you're using two different keypairs : one to encrypt, and another one to decrypt. That can't work. I even gave you a code sample showing that everything ran fine if you used the same keypair.
KeyPairGenerator.generateKeyPair() generates a keypair. Calling this method twice will get you two different keypairs: it uses a random number generator internally to generate always different keypairs.
You must generate a keypair once, store it in a variable, and use this variable to encrypt and decrypt.
You should read the documentation of the classes and methods you are using. The documentation of generateKeyPair says:
This will generate a new key pair
every time it is called.
Add this main method to EncryptAndDecrypt, and execute it. You'll see that evrything works fine.
public static void main(String[] args) throws Exception {
String s = "hello";
Cipher cipher = createCipher();
KeyPair keyPair = generateKey();
byte[] b = encrypt(s, cipher, keyPair);
String s2 = decrypt(b, cipher, keyPair);
System.out.println(s2);
}
The problem lies in the way you're using this class.
The useDecrypted method does the following:
Cipher cip = EncryptAndDecrypt.createCipher(); // create a Cipher object using EncryptAndDecrypt
KeyPair key = EncryptAndDecrypt.generateKey(); // generate a KeyPair using EncryptAndDecrypt
// call EncryptionApp.getEncrypted() to get an encrypted text, then decrypt this encrypted text
// using the keypair created above.
decryptedVote = EncryptAndDecrypt.decrypt(EncryptionApp.getEncrypted(), cip, key);
And the getEncrypted method does the following:
String plainText = "der"; // create some plain text
// create a Cipher instance. Is it the same algorithm as the one in useDecrypted?
// we don't know, because it uses another, unknown, Safety class
Cipher cip = Safety.createCipher();
// create a new KeyPair instance. Is it the same KeyPair as the one in useDecrypted?
// No : another keypair is generated. There is no way something encrypted using a keypair
// will decrypt correctly with another keypair.
KeyPair key = Safety.generateKey();
encyptedByte = Safety.useRSA(plainText, cip, key);
So, in short, you use two different keypairs : one to encrypt and the other to decrypt. That can't work.
Also, note that in encrypt, you transform your string into a byte array using the UTF8 encoding, whereas in decrypt, you transform the byte array into a String using the default platform encoding. You should use UTF8 for both, and thus use the following code in decrypt :
String decryptedPlainStr = new String (decryptedPlainText, "UTF8");
Have you googled? A lot of people have this problem when the key to encrypt is not the same as the key to decrypt. It seems like you generate new keys all the time instead of using the same key to decrypt that you used for encryption.
I was getting this error and it turned out in my case to be that the base 64 string I was sending as a parameter contained some characters that were being altered because of being in a URL. The solution turned out to be URL encoding the parameter.

Categories