BouncyCastle signature value does not match with dotNET signature value - java

I have a problem with the "SHA1withRSA" signature using Bouncy Castle on Android: Bouncy Castle signature value does not match with .NET signature value.
I tried to use many algorithms like "SHA1withRSA", "SHA1withRSAAndMGF1" or "SHA1withRSA/ISO9796-2" with no satisfactory results.
If I use the same message, same algorithm and the same private key, the result must be identical in .NET and Bouncy Castle... shouldn't it?
Whats wrong in my code???
Getting the private key:
...
ks = KeyStore.getInstance("PKCS12");
...
key = ks.getKey(alias, senha.toCharArray());
if (key instanceof PrivateKey) {
privateKey = (PrivateKey) key;
}
Signature method:
public String signer(String txt, String alg) throws Exception {
Signature signer = Signature.getInstance(alg, new BouncyCastleProvider());
signer.initSign(privateKey);
signer.update(txt.getBytes("UTF-8"));
return Base64.encodeToString(signer.sign(), Base64.NO_WRAP);
}
My app must use XMLDSIG protocol to send XML to a government's web service.

Value being signed can have some random padding data (is your signature is always the same on one side?). You should generate signature on one side and try to verify on the other.
This is especially the case for the following algorithms:
"SHA1WITHRSAANDMGF1"
"SHA224WITHRSAANDMGF1"
"SHA256WITHRSAANDMGF1"
"SHA384WITHRSAANDMGF1"
"SHA512WITHRSAANDMGF1"
Install the Bouncy Castle provider and see if the signature can be verified with the public key.

Related

What's the detail in "SHA1withRSA"?

Innocently, I thought "SHA1withRSA algorithm" was simply operating the plainText with "SHA1", and use RSA/pkcs1padding to encrypt the result of "SHA1"。However, I found I was wrong until I wrote some java code to test what I thought.
I use RSA publickey to decrypt the signature which I use the corresponding privatekey to sign with "SHA1withRSA algorithm" . But I found the result is not equal to "SHA1(plainText)", below is my java code:
String plaintext= "123456";
Signature signature=Signature.getInstance("SHA1withRSA",new BouncyCastleProvider());
signature.initSign(pemPrivatekey);
signature.update(plaintext.getBytes());
byte[] sign = signature.sign();
//RSA decode
byte[] bytes = RsaCipher.decryptByRsa(sign, pemPublickey);
String rsaDecodeHex=Hex.toHexString(bytes);
System.out.println(rsaDecodeHex.toLowerCase());
String sha1Hex = Hash.getSha1(plaintext.getBytes());
System.out.println(sha1Hex);
//rsaDecodeHex!=sha1Hex
Easy to find that rsaDecodeHex!=sha1Hex, where
rsaDecodeHex=3021300906052b0e03021a050004147c4a8d09ca3762af61e59520943dc26494f8941b
and
sha1Hex=7c4a8d09ca3762af61e59520943dc26494f8941b 。
So, What's the detail in "SHA1withRSA" ?
The digital signature algorithm defined in PCKS#1 v15 makes a RSA encryption on digest algorithm identifier and the digest of the message encoded in ASN.1
signature =
RSA_Encryption(
ASN.1(DigestAlgorithmIdentifier + SHA1(message) ))
See (RFC2313)
10.1 Signature process
The signature process consists of four steps: message digesting, data
encoding, RSA encryption, and octet-string-to-bit-string conversion.
The input to the signature process shall be an octet string M, the
message; and a signer's private key. The output from the signature
process shall be a bit string S, the signature.
So your rsaDecodeHex contains the algorithm identifier and the SHA1 digest of plainText

Generate token with RS256 and Jwts.builder().signWith() produces invalid signature

Using the JWT Java library and producing a token with the RS256 algorithm, I always get an invalid signature with the jwt.io debugger. Here is my example code, I tried to make it as simple as possible to start with my current project:
// Create a legitimate RSA public and private key pair:
KeyPair kp = RsaProvider.generateKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();
String jwt = Jwts.builder().setSubject("Joe").signWith(SignatureAlgorithm.RS256, privateKey).compact();
This code is inspired from the test class here.
Any idea what I could be missing?
The jwt.io debugger expects that you provide the public key associated with the private key used to sign the token encoded in the Public Key file (PKCS#8) format.
Ensure that you specify it using exactly that format, an example follows:
-----BEGIN PUBLIC KEY-----
BASE64 DATA
-----END PUBLIC KEY-----

How can I create a Java KeyStore from an existing set of certificates and keys without using command line tools?

I obtain a randomly generated RSA certificate and private key as strings from another service, and I would like to use those strings to create a Java KeyStore. All of the examples I see involve saving these strings to files, using the openssl and keytool command line tools to create the keystore on disk, and then loading the resulting KeyStore into memory (like here). However, it makes more sense for my purposes to create the KeyStore entirely in memory.
To that end, I am trying to use the Java Security API. I am able to convert the certificate string into an instance of the java.security.cert.Certificate class, but I am unable to convert the private key into an instance of java.security.PrivateKey. Here's method I am trying to create:
private PrivateKey generatePrivateKey (String newKey)
throws NoSuchAlgorithmException,
InvalidKeySpecException, IOException {
//Configuring the KeyFactory to use RSA
KeyFactory kf = KeyFactory.getInstance("RSA");
//Convert the key string to a byte array
byte[] keyBytes = newKey.getBytes();
KeySpec ks = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey key = kf.generatePrivate(ks);
return key;
}
Where the value of newKey is something like "-----BEGIN RSA PRIVATE KEY-----\nMIIEow...gNK3x\n-----END RSA PRIVATE KEY-----".
When I run the code, I receive the following exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)
... 30 more
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:341)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:367)
at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)
at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
at sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:316)
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:213)
... 32 more
This particular error is very similar to this stackoverflow question, and I would be grateful if that question were resolved, but I also want to know if my higher level goal (creating a JKS programmatically and solely in memory using Java) is feasible, and if so, whether I am on the right path.
you need to decode base64 if your key is in the base64 representation:
KeySpec ks = new PKCS8EncodedKeySpec(Base64.decodeBase64(newKey));
you can use org.apache.commons.codec.binary.Base64 to do this.
if you want to generate keyPair you can you this code:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// extract the encoded private key, this is an unencrypted PKCS#8 private key
byte[] encodedprivkey = keyPair.getPrivate().getEncoded();
System.out.println(Base64.encodeBase64String(encodedprivkey));

RSA encryption in Java, decrypt in PHP

Assume I have the following Java code to generate a Public-private keypair:
KeyPairGenerator generator = KeyPairGenerator.getInstance ("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize (1024, random);
KeyPair pair = generator.generateKeyPair();
RSAPrivateKey priv = (RSAPrivateKey)pair.getPrivate();
RSAPublicKey pub = (RSAPublicKey)pair.getPublic();
// Sign a message
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign (priv);
dsa.update ("Hello, World".getBytes(), 0, "Hello, World".length());
byte[] out = dsa.sign();
/* save the signature in a file */
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(out);
sigfos.close();
How would one go about and decrypt the file "sig" in PHP? I've read the post: https://stackoverflow.com/a/1662887/414414 which supplies a function to convert a DER file to PEM (Assume I also save the public key from Java).
I have tried something like:
$key = openssl_pkey_get_public ("file://pub_key.pem");
$data = null;
openssl_public_decrypt ( file_get_contents ("sig"), $data, $key);
echo $data, "\n";
It successfully decrypts the message, but it is many weird characters.
Our scenario is a Java client that is sending messages to a PHP server, but encrypts the data with a private key. PHP knows about the public key, which it should use to decrypt and validate the message.
I've read a lot of posts regarding this issue here on SO, but I've come to realize that this is a bit specific issue, especially if there's different algorithms in use, etc. So sorry if this may be a duplicate.
Any feedbacks are greatly appreciated!
an "RSA signature" is usually more than just "encrypt with private key, decrypt with public key", since Public key protocols like PKCS#1 also specify padding schemes, and all signature schemes will encrypt a digest of the message, instead of the full message. I cannot find any documentation if java's signature scheme uses the signature padding scheme specified in PKCS#1, but my suspicion is that it is.
If it is, you will instead want to use the openssl_verify method in PHP, documented here. This will return a 0 or 1 if the signature is invalid or valid, respectively.
In the event that Java does not use a padding scheme, your issue is that the data encrypted in the signature is a hash of the message, instead of the message itself (you can see in the Java code that it uses the SHA-1 hash algorithm). So on the PHP side, you will need to take the sha1 hash of your message using the sha1 method with $raw_output set to true, and compare those strings to ensure your message is valid.
From the snippet
$key = openssl_pkey_get_public ("file://pub_key.pem");
It looks like you're referencing the public key, which would be the wrong one to decrypt. Double check ?

XML Digital Signature validation

I was trying to validate an XML signature.
The validation according to this tutorial works fine.
But I also tried to a second approach. To verify it with the verify method of the Signature class
I extracted the signature and the certificate from the xml file, and I did the following:
public static boolean checkSignedFile(byte[] data, byte[] sigToVerify,
byte[] cert, String algorithm) throws CertificateException,
NoSuchAlgorithmException, InvalidKeyException, SignatureException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate c = (Certificate) cf
.generateCertificate(new ByteArrayInputStream(cert));
PublicKey pk = c.getPublicKey();
Signature sig;
boolean verifies = false;
sig = Signature.getInstance(algorithm);
sig.initVerify(pk);
sig.update(data);
verifies = sig.verify(sigToVerify);
return verifies;
}
the result was false. The signature did not verify. What could be the reason for that?
You can't verify XMLDsig like this. It wouldn't work. The signature is not calculated over the raw XML. It has to go through canonicalization, digest etc.
What do you use for data[]? To get it right, you almost have to rewrite the XMLDsig library.
If data[] is the content of the signed XML file, what is sigToVerify?
XMLSig creates a Signature-Element (SignedInfo) that contains the digest of each Element to be signed and meta-information like used canonicalization/transformation algorithms. Then the digest of this SignedInfo-Elemnt is calculated and signed.
Hence, if sigToVerify is the signature created by a XMLSignature implementation it must not be equal to the signature of the complete XML file.
Here is a more complete explanation. And if your interested, take a look at the specification.

Categories

Resources