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?
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 am trying to duplicate an encryption process that is working in Java over to iOS/OSX.
My Java code is as follows:
PublicKey publicKey = KeyFactory.getInstance("RSA").
generatePublic(new RSAPublicKeySpec(firstKeyInteger, secondKeyInteger));
// This always results in the public key OpenSSLRSAPublicKey{modulus=2b3b11f044.....58df890,publicExponent=10001}
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING");
String stringToEncode = "EncodeThisString";
byte[] bytesToEncode = stringToEncode.getBytes("UTF-8");
cipher.init(cipher.PUBLIC_KEY, publicKey);
byte[] encrypted = cipher.doFinal(plain);
The first challenge i'm struggling with is how to use the public key in iOS. Can I just dump the modulus into NSData and use it? Or must I store it in the keychain first? (I don't really need to use the keychain unless I must). Or is there a method similar to generatePublic() were I can recreate the public key using the 2 integers?
Then would I use SecKeyEncrypt to encrypt? Whenever I add this to my project I get Implicit declaration warnings even though I import the Security framework.
Thanks
EDIT -----
I think I have managed to get a Base64 encoded public key, which I believe is what is in a PEM certificate. Now, how to use it.
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);
I am using BouncyCastle to generate a DSA signature but using the native JCE to verify the it.
NOTE: I am working with a j2me client that does not natively support signing hence the need for BouncyCastle)
So, on the client the signature is generated as follows:
DSASigner sig = new DSASigner();
sig.init(true, privateKey);
String plaintext = "This is the message being signed";
BigInteger[] sigArray = sig.generateSignature(plaintext.getBytes());
...
sigArray contains 2 BigIntegers r and s.
This signature then has to be transmitted to a server which uses native JCE to verify the sig. On the server side, using the native Java JCE, it should be possible to verify a signature as follows:
...
Signature sig = Signature.getInstance("SHA1withDSA");
byte[] sigbytes = Base64.decode(signature);
sig.initVerify(publicKey);
sig.update(plaintext.getBytes());
sig.verify(sigbytes)
The problem am having is: how do i encode sigArray into a format that can be sent to the pc/server as a single Base64 string (instead of separately as r and s) that can then be verified on the server using the native JCE method show in the second snippet of code?
So far i have tried to create DERObjects from the r,s arrays (separately, together as one array, encoded) but still no luck. Anybody faced this before? How did you tackle it?
According to Cryptographic Message Syntax Algorithms (RFC 3370) the DSA signature encoding is an ASN.1 sequence containing both integers r and s:
Dss-Sig-Value ::= SEQUENCE {
r INTEGER,
s INTEGER }