Yes, I know that Bouncy Castle is probably the only reasonable library for doing crypto in Java 8. However …

I can remember my first computer - Sinclair ZX Spectrum+ - with incredible 48KB RAM. The experience is still inside me and I find it hard to justify using 1.5+MB of Java code where 10 lines could do the trick. I talk about doing RSA signatures in Java 8.

Using system Java libraries for crypto is not fashionable as Bouncy Castle gives you a much wider set of functions. However, if you just need to do a bit of cryptography, Bouncy Castle may be an unnecessary dependency. Here’s how you can sign documents and verify signatures.

Signature Verification

The first thing you need is some input data and let’s start with signature verification:

  1. we need a signature - the code below delivers it in the base64 form so we will decode it as well;
  2. some data that the signature is for - again, it’s base64 data as this snippet is for JWS-like signature verification; and
  3. and we need a public key - we got it in the form of X.509 certificate, from which we extract the public key.

Note: the example below is for 4,096 bit RSA and export crypto restrictions may kick in. So if you haven’t done it yet, and experience weird problems, you may need to install an unlimited crypto policy for your Java installation. Have a look here, it’s pretty simple (once you find the right folder:) ): Java Cryptography Extension (JCE) Unlimited Strength



import javax.security.*;
import java.security.*;

// create a Base64 decoder instance
Base64.Decoder b64decoder = Base64.getDecoder();
// read a certificate from a base64 string
byte[] derCertificate = b64decoder.decode("MIIC4TCCAcmgAwIBAgIQCaWR/8BMflFb9vqNRGJ3cTANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDAppLTllN2M2NjEyMB4XDTE2MTAwMzIxNDYyNloXDTQxMDkyNzIxNDYyNlowFTETMBEGA1UEAwwKaS05ZTdjNjYxMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANdYTkecZIOHXR2iEIV24ISKP578ClBkSM1BM+EL3qYvx3ZuiqGxGqrUcAHKfUv4OjzXmfhprEAkemU3ao0d/rf0SBHuq99HTC3mecnvXMBhKe1ovd9Df5leqiXeSGOU6Z8gMYR15q3kWyAdnzS5+trGBvrtQxKx15CgLK3Ncw4PEGMzTivcJxV4oroV0mZzvHSaQQLl14lUNTEzs5OFTNYe8HZaSJI0VPlKPcDun25FDKOZMa3nE2l1m9ZttyWrsp4oghrUrNsaJXBC5lhBfD6oYQaOIWjlVnRv/gvSUp1X6VMupQaas78qXubI7OD74w26Oe/nFpUrLmdfC2eeAN0CAwEAAaMtMCswEgYDVR0TAQH/BAgwBgEB/wIBADAVBgNVHREEDjAMggppLTllN2M2NjEyMA0GCSqGSIb3DQEBCwUAA4IBAQCBjTxnNQX06M4qUAoCERmwJ/7k3xTg0FCqP0O58fhXb9oKMpJIZWVAbUYctcZCi0freKIyOQ+JF7J6CsuSkrd+ck8c4aYwRqX9YmBoyt7r14QYIKnvVbuudkT/djR3prI3Li4zT3VMT8FakckEP6tKrkrAjRQ+6KKT5YoM1TQqY0WEnzjqBtDva2CQvClj9rtfcNAnV2V24Qii2NUgOwanUudMqrutOsK2E3wt+Sa5t/NjAxva0FuO2101UISW6r28brvKTNN6IwW23662coEyQwJNA+o2TF9VwipzRbmb+g6lTDxdee77yIfOcssg03n7MMwKKt2KXGt7IE2btCgt");
// create a X509Certificate instance
X509Certificate testX509 = X509Certificate.getInstance(derCertificate);
//here is the data
String datab64 = "eyJ1c2VybmFtZSI6ICI2a3JjbWlhY2xR6NTVwcWNyd3d4ODllMmY4d2JpM3o3M29la2M2cWl1YnYwMDRlczAiLCAiYXV0aGVudGljYXRpb24iOiAiY2hhbGxlbmdlIiwgImFwaWtleSI6ICJ3dnhlemVpZnVwb2JpMnNuN3Bha2wyc3QxdngycTk3bnpvNGRia2cwdHlmaHF0ZXBhamU5Znl1c252dm1kdWxyIiwgInJlc3BvbnNlIjogInpxOTBubmdsbm1qbHljdXF2OGNxOTE5Zm1pOWxudjlueTdhM2hnMGVjdGU4aXJvNmphaXNzeTA0anZpaXY5Nmh4cGV5aDM4ZnMxcnZ2MG42bHM0cXV5bTlwMXNiM2F1MHB5dGUzbzRxNWZteXNkM2VuOGZncWpkNjllaTduOHlyIn0=";
// and the last bit is the signature we verify
String signatureb64 = "sUfTwtwoz0YLxwKfzUqtfLiatKiGHLvb7RasHTNrA7IDsNpMIU/KLB/TtvI8vXhRsJVxuDa3WsXRM9kjIuXIOBks/8WEi1tr4Yb0aGaFhIBGoThdUypXgxuEi2VOGwz2Ayv2UAi0Et9Vz/CORdH+sWFdAyZBkYbxLdYExciGldK/caVEjC4WFaMwb6c6blP8xQEUnN2kHEutuF3ROi2aknIYY3VQgbzPJCYUTqgviS+2ZabEL1FJEyahb2VZqO72+sOjqHT4wdDFrP+GuXc5SgbrZ9Jv0JHVP5H7h5kUeKIe/DaRb3865s0U0668+XNcSf8Yzvo+TVSCU5cG8+cBSA==";
byte[] signature = b64decoder.decode(signatureb64);

// create a crypto instance - in this case we have SHA256 as hash algorithm
// it's worth noting that the default padding is PKCS1.5 - if someone asks, OAEP is available as well
Signature signAlg = Signature.getInstance("SHA256withRSA");
// we will initialize the crypto signature instance with the public key extracted from the certificate
signAlg.initVerify(testX509.getPublicKey());
// we will load the data - this signature was computed from the base64 data, similar to JWS approach
signAlg.update(datab64.getBytes());
// and we simply verify the signature
boolean result = signAlg.verify(signature);

Signing

What is the difference for creating the signature? Well there are 2 points here:

  1. you need a private key that will be used with signAlg.initVerify() method
  2. instead of the verify() method, you will call the sign() method, which returns a byte array



import javax.security.*;
import java.security.*;

// create a Base64 decoder instance
Base64.Decoder b64decoder = Base64.getDecoder();

//here is the data
String datab64 = "eyJ1c2VybmFtZSI6ICI2a3JjbWlhY2xR6NTVwcWNyd3d4ODllMmY4d2JpM3o3M29la2M2cWl1YnYwMDRlczAiLCAiYXV0aGVudGljYXRpb24iOiAiY2hhbGxlbmdlIiwgImFwaWtleSI6ICJ3dnhlemVpZnVwb2JpMnNuN3Bha2wyc3QxdngycTk3bnpvNGRia2cwdHlmaHF0ZXBhamU5Znl1c252dm1kdWxyIiwgInJlc3BvbnNlIjogInpxOTBubmdsbm1qbHljdXF2OGNxOTE5Zm1pOWxudjlueTdhM2hnMGVjdGU4aXJvNmphaXNzeTA0anZpaXY5Nmh4cGV5aDM4ZnMxcnZ2MG42bHM0cXV5bTlwMXNiM2F1MHB5dGUzbzRxNWZteXNkM2VuOGZncWpkNjllaTduOHlyIn0=";

// generate an RSA key pair - i.e., RSA private key
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(4096);
KeyPair keyPair = kpg.genKeyPair();

// create an instance of an RSA signature class - in this case we have SHA256 as hash algorithm
// it's worth noting that the default padding is PKCS1.5 - if someone asks, OAEP is available as well
Signature signAlg = Signature.getInstance("SHA256withRSA");
// we will initialize the crypto signature instance with the created private key
signAlg.initSign(keyPair.getPrivate());
// we will load the data - this signature will computed from the base64 data, similar to JWS approach
signAlg.update(datab64.getBytes());
// and we simply create a signature
byte[] result = signAlg.sign();

// and maybe base64 it for easy handling
Base64.Encoder b64encoder = Base64.getEncoder();
String signatureOut = b64encoder.encodeToString(result);