I have a key pair already, public and private. How do I actually use the java.security.Signature to do verification of a string I signed with one of the keys?
Edit:
I have both the keys as Strings. The verify method, it is actually
verify(byte[] signature)
The javadoc says:
verify(byte[] signature) Indicates whether the given signature can be
verified using the public key or a certificate of the signer.
How would I make that signature recognize which public/private key to use for that verifying, before I call the verify method? In other words, how do I turn my string keys into key objects that would get accepted by signature?
Use KeyFactory to translate key specifications to objects.
Call Signature.getInstance(algName) to get a signature instance.
Use Signature's initVerify method to associate a key for signature verification.
Use update to feed the Signature bytes.
Finally, call verify.
Profit
From the KeyFactory javadoc:
The following is an example of how to use a key factory in order to instantiate a DSA public key from its encoding. Assume Alice has received a digital signature from Bob. Bob also sent her his public key (in encoded format) to verify his signature. Alice then performs the following actions:
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
Signature sig = Signature.getInstance("DSA");
sig.initVerify(bobPubKey);
sig.update(data);
sig.verify(signature);
Related
I have this JAVA code & I need to write the same thing in php:
public static String signMsg(String msg, String privateKey)
throws Exception {
byte[] bytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
Signature ps = Signature.getInstance("SHA256withRSA");
ps.initSign(kf.generatePrivate(spec));
ps.update(msg.getBytes("UTF-8"));
byte[] sigBytes = ps.sign();
return Base64.getEncoder().encodeToString(sigBytes);
}
Any idea how to do that?
Thanks in advance :)
Regarding your first approach: A signature is created with the private key. The public key is used to verify the signature. Regarding your second approach: A HMAC is not the same as a signature.
The Java code loads a private key in PKCS8 format, PEM encoded without header and footer. In the PHP code the key can be read in the same format and encoding. Alternatively, the key can be loaded in PKCS#1 format. Regarding the encoding, a PEM or DER encoded key is also accepted.
Additionally, it must be specified which algorithm is used for signing. The Java code applies RSA with PKCS#1 v1.5 padding and SHA-256 as digest. Furthermore, the generated signature is Base64 encoded. In order for the PHP code to provide the same RSA signature, the same parameters must be used.
Note that signing does not necessarily generate the same signature using the same message and the same key. It depends on the algorithm. However, in the case of RSA with PKCS#1 v1.5 padding, always the same signature is generated (deterministic). For PSS, on the other hand, a different signature is generated each time (probabilistic).
The following PHP code uses the PHPSECLIB and generates the same signature as the Java code:
use phpseclib3\Crypt\RSA;
$privateKey= 'MIIEvg...';
$signatureB64 = base64_encode( // Base64 encode signature
RSA::load($privateKey)-> // Choose RSA, load private PKCS8 key
withHash('sha256')-> // Choose SHA-256 as digest
withPadding(RSA::SIGNATURE_PKCS1)-> // Choose PKCS#1 v1.5 padding
sign('The quick brown fox jumps over the lazy dog') // Sign messsage
);
print($signatureB64);
The plain text is signed using java.security.Signature. Below is the code used to sign the plain text
public String getSignature(String plainText) throws Exception
{
KeyStore keyStore = loadKeyStore(); // A local method to read the keystore file from file system.
PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD.toCharArray());
Signature privateSignature = Signature.getInstance(SIGNATUREALGO);
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes("UTF-8"));
byte[] signature = privateSignature.sign();
return String.valueOf(signature);
// KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD and SIGNATUREALGO are all constant Strings
}
Note 1: I found online a way to verify the signature using the public key Java Code Examples for java.security.Signature#verify(). But this is not what I require.
Note 2: I also found a ways to encrypt and decrypt as mentioned here RSA Signing and Encryption in Java. But the use case I have in hand is to get the original plain text from a signed data. Is that possible?
No, you can't retrieve the original content from just the signature.
The signature alone does not contain enough information to restore the original clear text, no matter what keys you have access to.
The basic idea of a signature is to send it together with the clear text. That means the clear text will be visible, but the signature can be used to verify that the message was written (or at least signed) by who claims to have done so and has not been tampered with since then.
Signing something is different from encrypting it. The two often uses the same or related technologies and both fall under cryptography.
I want to sign a message using RSA private key. This message will be verified by someone else maybe using another programming language other than Java.
I will use the java.security.Signature class to sign the messsage.
The code will be :
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(key); //RSA private key
privateSignature.update(text.getBytes("UTF8")); //text is the clear text string
byte[] signature = privateSignature.sign();
return Base64.getEncoder().encodeToString(signature);
When I check the result and used the publickey to decrypt the signature, I found that padding bytes are added before the SHA256 digest.
My Question is : is the resulted signature generated in a standard way so that it can be verified with the public key in widely used programming languages or is it java specific?
I have an X509Certificate, and a byte array that represents a raw signature created by a remote server (which has my private key).
I'm trying to add this signature with PDFBox 2.0.18. For that, I need CMSSignedData object.
How do I generate CmsSignedData without private key (only signature, public key, certificate)?
I am having issues signing an rsa signature. I have a signature that has been encrypted with a private key. I have an issue when trying to validate it with the public key however. I get the following exception:
java.security.SignatureException: Signature length not correct: got 336 but was expecting 128
at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:189)
at java.security.Signature$Delegate.engineVerify(Signature.java:1219)
at java.security.Signature.verify(Signature.java:652)
at XmlReader.main(XmlReader.java:65)
I have retrieved my signature and public key the following way:
BigInteger modulus = new BigInteger(Base64.getDecoder().decode(publicKeyString));
BigInteger exponent = new BigInteger(Base64.getDecoder().decode("AQAB"));
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
byte[] sigToVerify = Base64.getDecoder().decode(signatureString);
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initVerify(pubKey);
boolean verifies = sig.verify(sigToVerify);
The application fails at the last line. Any thoughts as to where this exception is caused?
UPDATE:
Added data for signature to be verified:
String data = "...." //hidden since sensitive data
byte[] dataBytes = Base64.getEncoder().encode(data.getBytes());
dataBytes = Base64.getDecoder().decode(dataBytes);
Before calling sig.verify(sigToVerify) you should call
sig.update(data);
passing the data you're verifying signature for.
And make sure that calling verify in your argument you have signature bytes only.
Well, the signature size is obviously not correct. This means that at least the signature is not formatted correctly; an RSA signature by itself is always the size of the modulus in bytes (identical to the key size, in your case a short 1024 bit key).
algrid is right that usually you'd have to update the signature first. So it would be logical that the input of the signature is prefixed with the data that was signed. In that case the last 128 bytes are probably the signature, and the bytes before that are the data.
It is also possible that the signature is not a "raw" signature but a formatted signature, e.g. using the PKCS#7 / CMS syntax or PGP syntax. In that case you would need an library to decode it.
Note that you cannot verify a signature without the data that was signed, or at least the hash over the data. You can only check the correctness of the signature in that case (by validating the padding performed before modular exponentiation for RSA, in case we need to go into details).