We're porting PHP (Yii framework) to Java (Play! Framework 2.2.x) and came across this piece of code:
$sign = base64_decode($sign64);
// $cert is a string read from a file.
$pubkey = openssl_get_publickey($cert);
// $data is composed of incoming username data, time, etc.
if(openssl_verify("$data", $sign, $pubkey) != 1) {
$this->verifies = false;
} else {
$this->verifies = true;
}
openssl_free_key($pubkey);
I've shortened this code so that it contains the essentials.
$data is a field sent with the GET request.
$sign64 is another field sent with this GET request. It is provided by another server and is a signature that verifies the data sent to be valid.
The $cert file contains the string "-----BEGIN CERTIFICATE-----".
So I must ask; what is a standard way of implementing this verification code in Java?
Specifically; we're using the Play! Framework. So far I have done the following:
String sign = new String(javax.xml.bind.DatatypeConverter.parseBase64Binary(sign64));
java.io.FileInputStream filestream = new java.io.FileInputStream(new java.io.File("certificate.crt"));
java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509");
java.security.cert.Certificate cert = cf.generateCertificate(filestream);
java.security.PublicKey pubkey = cert.getPublicKey();
try
{
cert.verify(pubkey, sign64); // This seems to be the closest to what I desire...
}
catch (...)
{
// Here I output the error message...
}
What I end up with is a "NoSuchProviderException". It shows some cryptic text which is the same as the string "sign". My question is: Am I doing something fundamentally wrong? Is parseBase64Binary correct? Is the method of signing correct? It shouldn't be; since I do not sign the data variable at all here. How do I do this correctly in Java/Play! Framework?
The issue was resolved with the following code: (This is for those who have the same question)
import java.security.*;
// ...
public static boolean verifySignature(String data, PublicKey key, byte[] signature) throws Exception {
Signature signer = Signature.getInstance("SHA1withRSA");
signer.initVerify(key);
signer.update(data.getBytes());
return (signer.verify(signature));
}
The public key is read in using the code given in the question. The signature uses: byte[] sign = java.util.Base64.getDecoder().decode(sign64);
The biggest challenge was finding out which algorithm to use. The example I got the above code from had "SHA1withDSA", much was tried. Eventually the string "SHA1withRSA" worked. It correctly verifies the signed data from our data provider.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With