Using RSA with Java and C# - java

I'm currently working on a Project where I wanna use RSA for authentification. But as I'm using Java for my Server and C# for my Client I have alot of problems with creating or better initilize the PublicKey class from java.security in Java. I've already used mutlipe solutions for both languages and most of them are working themself.
So thats my basic C# RSA Client:
public static void NewKeyPair()
{
var csp = new RSACryptoServiceProvider(1024);
//how to get the private key
var privKey = csp.ExportParameters(true);
//and the public key ...
var pubKey = csp.ExportParameters(false);
_ = privKey.Modulus;
_ = pubKey.Modulus;
}
public void Encrypt(string text, string publicKey)
{
RSAParameters para = new RSAParameters();
para.Modulus = Convert.FromBase64String(publicKey);
//conversion for the private key is no black magic either ... omitted
//we have a public key ... let's get a new csp and load that key
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(para);
//for encryption, always handle bytes...
var bytesPlainTextData = System.Text.Encoding.Unicode.GetBytes(text);
//apply pkcs#1.5 padding and encrypt our data
var bytesCypherText = csp.Encrypt(bytesPlainTextData, false);
//we might want a string representation of our cypher text... base64 will do
var cypherText = Convert.ToBase64String(bytesCypherText);
}
public static void Decrypt(string encryptedText, RSAParameters para)
{
var bytesCypherText = Convert.FromBase64String(encryptedText);
//we want to decrypt, therefore we need a csp and load our private key
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(para);
//decrypt and strip pkcs#1.5 padding
var bytesPlainTextData = csp.Decrypt(bytesCypherText, false);
//get our original plainText back...
var plainTextData = System.Text.Encoding.Unicode.GetString(bytesPlainTextData);
}
So this is basically from a Microsoft doc if I remmeber right.
Meanwhile that's my Java code:
public static KeyPair gen() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
return keyGen.generateKeyPair();
}
public static String encrypt(String message, PublicKey pk) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pk);
return Base64.getEncoder().encodeToString(cipher.doFinal(message.getBytes()));
}
public static String decrypt(String encryptedText, PrivateKey pk) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pk);
return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)));
}
So for connecting those I'm exporting the Key first in C# with
Convert.ToBase64String(csp.ExportSubjectPublicKeyInfo()
After writing this to Console I paste it into this code snip in Java:
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(""));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// Encrypting
String encrypted = encrypt("Hello", pubKey);
Afterwards I'm checking everythink by decrypting it again in C# ( Mostly failing before at keyFactory.generatePublic(keySpec); )
Also though aleady that's maybe caused by the byte system as c# using unsigned and java signed, but don't really have an idea how to fix that.
Someone has an idea how to solve this problem? c:

Related

Get this "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" combination for ruby

I have this java code for encryption and decryption, which I want to change/convert to Ruby code. I looked up in OpenSSL gem but dint find the "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" combination available for ruby. How do I implement it?
public class EncryptDecryptService {
public String encryptRequestObject(RequestObject requestObject) throws UnsupportedEncodingException, FileNotFoundException, CertificateException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
PublicKey publicKey = getPublicKey(requestObject.getKeyFilename());
byte[] message = requestObject.getString().getBytes("UTF-8");
byte[] secret = encrypt(publicKey, message);
return Base64.encodeBase64String(secret);
}
public String decryptRequestObject(RequestObject requestObject) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
PrivateKey privateKey = getPrivateKey(requestObject.getKeyFilename(), requestObject.getKeyPassword());
byte[] cipherText = Base64.decodeBase64(requestObject.getString());
byte[] decrypted = decrypt(privateKey, cipherText);
return new String(decrypted, "UTF-8");
}
private PublicKey getPublicKey(String filename) throws FileNotFoundException, CertificateException {
FileInputStream fin = new FileInputStream(filename);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) factory.generateCertificate(fin);
PublicKey publicKey = certificate.getPublicKey();
return publicKey;
}
private PrivateKey getPrivateKey(String filename, String password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {
FileInputStream fin = new FileInputStream(filename);
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(fin, password.toCharArray());
String str = ks.aliases().nextElement();
PrivateKey privateKey = (PrivateKey) ks.getKey(str, password.toCharArray());
return privateKey;
}
private byte[] encrypt(PublicKey key, byte[] plainText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText);
}
private byte[] decrypt(PrivateKey key, byte[] cipherText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
}
OAEP uses several parameters, including two digests, one for OAEP (i.e. for hashing the OAEP label) and one for the mask generation function (MGF1), see RFC8017, sec. 7.1.
The identifier RSA/ECB/OAEPWithSHA-256AndMGF1Padding is ambiguous and depends on the provider. For example the SunJCE provider uses SHA-256 as OAEP digest and SHA-1 as MGF1 digest, the BouncyCastle provider uses SHA-256 for both digests.
The following is an example of the encryption with the Java code and the decryption with the Ruby code (the opposite direction is analog).
On the Java side the SunJCE provider is used WLOG and the digests involved are determined:
String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNvs/qUMjkfq2E9o0qn03+KJE7" +
"ASczEbn6q+kkthNBdmTsskikWsykpDPnLWhAVkmjz4alQyqw+mHYP9xhx8qUC4A3" +
"tXY0ObxANUUKhUvR7zNj4vk4t8F2nP3erWvaG8J+sN3Ubr40ZYIYLS6UHYRFrqRD" +
"CDhUtyjwERlz8KhLyQIDAQAB";
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
byte[] plaintext = "The quick brown fox jumps over the lazy dog".getBytes(StandardCharsets.UTF_8);
byte[] ciphertext = encrypt(publicKey, plaintext);
System.out.println("Ciphertext: " + Base64.getEncoder().encodeToString(ciphertext));
with
private static byte[] encrypt(PublicKey key, byte[] plainText) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
OAEPParameterSpec oaepParameterSpec = cipher.getParameters().getParameterSpec(OAEPParameterSpec.class);
MGF1ParameterSpec mgf1ParameterSpec = (MGF1ParameterSpec)oaepParameterSpec.getMGFParameters();
System.out.println("Provider : " + cipher.getProvider().getName());
System.out.println("OAEP-Hash : " + oaepParameterSpec.getDigestAlgorithm());
System.out.println("MGF1-Hash : " + mgf1ParameterSpec.getDigestAlgorithm());
return cipher.doFinal(plainText);
}
which corresponds to the posted encrypt() method (except for the additional output). The code produces (e.g.) the following output:
Provider : SunJCE
OAEP-Hash : SHA-256
MGF1-Hash : SHA-1
Ciphertext: WlozD9ojNRQafip41dpuuhBMe7ruH2FBWnMhbAaSuAtPDpHOUyKaAm6mO15BbvL3eTXyqfEQx29dYPJEbUr5T/WXs846PQN6g7Yv25EXGVbPCzc4aIbms76C1jP92wXNEGWMnu624Fq5W9MVXX75mfaY0Fjvrh5k/TFuO4AIxMk=
For completeness it should be mentioned that an explicit specification of the parameters is also possible with:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, key, oaepParameterSpec);
whereby this explicit specification is the more robust alternative because of the ambivalence described above.
After the digests have been determined (either because the provider is known or explicitly with the above output) a Ruby implementation can be made.
A possible OAEP implementation for Ruby is openssl-oaep.
With this, the Ruby code for decryption can be implemented as follows:
require 'openssl'
require 'openssl/oaep'
require 'base64'
private_key =
'-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAM2+z+pQyOR+rYT2
jSqfTf4okTsBJzMRufqr6SS2E0F2ZOyySKRazKSkM+ctaEBWSaPPhqVDKrD6Ydg/
3GHHypQLgDe1djQ5vEA1RQqFS9HvM2Pi+Ti3wXac/d6ta9obwn6w3dRuvjRlghgt
LpQdhEWupEMIOFS3KPARGXPwqEvJAgMBAAECgYADxGqqL7B9/pPOy3TqQuB6tuNx
4SOGm9x76onqUisoF7LhYqJR4Be/LAKHSR2PkATpKvOcMw6lDvCbtQ+j+rSK2PkN
4iDi1RYqbLUbZBS8vhrgU0CPlmgSSp1NBsqMK9265CaJox3frxmBK1yuf22RboIK
pqOzcluuA4aqLegmwQJBAP0+gM/tePzx+53DrxpYQvlfi9UJo7KeqIFL8TjMziKt
EaRGeOZ6UX/r6CQHojYKnNti7pjAwonsdwCTcv1yy7sCQQDP+/ww49VFHErON/MO
w5iYCsrM5Lx+Yc2JAjetCDpkMrRT92cgQ0nxR5+jNeh+gE2AmB9iKlNxsHJoRaPQ
lBRLAkEAl9hiZEp/wStXM8GhvKovfldMAPFGtlNrthtTCDvFXgVoDpgy5f9x3sIU
74WkPcMfSmyHpA/wlcKzmCTRTicHAQJBALUjq7MQ2tAEIgqUo/W52I6i55mnpZsU
pyOqcL8cqW5W0sNGd+SbdizTym8lJkX2jIlw8/RVFLOxjxLNhCzGqx0CQQDeUMnw
7KGP3F7BnbsXCp64YDdihzSO5X/Mfwxw6+S/pyKZ0/X4uwt24kZuoDnFzGWJYlea
sDQC6enIru+ne5es
-----END PRIVATE KEY-----'
key = OpenSSL::PKey::RSA.new(private_key)
label = ''
md_oaep = OpenSSL::Digest::SHA256
md_mgf1 = OpenSSL::Digest::SHA1
cipher_text_B64 = 'WlozD9ojNRQafip41dpuuhBMe7ruH2FBWnMhbAaSuAtPDpHOUyKaAm6mO15BbvL3eTXyqfEQx29dYPJEbUr5T/WXs846PQN6g7Yv25EXGVbPCzc4aIbms76C1jP92wXNEGWMnu624Fq5W9MVXX75mfaY0Fjvrh5k/TFuO4AIxMk='
cipher_text = Base64.decode64(cipher_text_B64)
plain_text = key.private_decrypt_oaep(cipher_text, label, md_oaep, md_mgf1)
print(plain_text) # The quick brown fox jumps over the lazy dog
with the original plaintext as output.

error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag

I'm trying to decrypt a string using RSA and my private key (size 4096). I'm getting the error:
console show error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
this es my code:
public String decrypt(String texte ) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
String stringKey = "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";
byte[] keyBytes = stringKey.getBytes();
String painText=texte;
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance(CIPHER_RSA_WITH_PADDING, PROVIDER);
cipher.init(DECRYPT_MODE,privateKey);
byte[] encryptedMessageBytes = Base64.decode(painText.getBytes().toString(),BASE64_FLAG);
return new String(cipher.doFinal(encryptedMessageBytes,0,encryptedMessageBytes.length));
}
the error is thrown out here:
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
I had a similar error when attempting to decode an RSA private key (2048 bits), but only on Marshmallow and older. Nougat and newer worked fine. This is because of the different Security Provider implementations on the different versions of Android. To work around this, I'm using the SpongyCastle provider.
I added the following line to my build.gradle:
implementation 'com.madgag.spongycastle:prov:1.58.0.0'
and the following line to my Application class's onCreate() method
Security.insertProviderAt(org.spongycastle.jce.provider.BouncyCastleProvider(), 1)
For completeness, here's my RSA decryption code:
fun decryptRsa(
cipherText: String,
key: String
): String {
val keySpec = PKCS8EncodedKeySpec(Base64.decode(key, Base64.DEFAULT))
val factory = KeyFactory.getInstance("RSA")
val privateKey: PrivateKey = factory.generatePrivate(keySpec) as RSAPrivateKey
val cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val encryptedBytes: ByteArray = Base64.decode(cipherText, Base64.DEFAULT)
return String(cipher.doFinal(encryptedBytes))
}

How to decrypt on the server side with java code?

I am using CryptoJS to encrypt at the client side I have created a fiddle of the client side code as following -
var onClick = function() {
var iv = "3ad5485e60a4fecde36fa49ff63817dc";
var key = "0a948a068f5d4d8b9cc45df90b58d382d2b916c25822b6f74ea96fe6823132f4";
var encrypted = CryptoJS.AES.encrypt("{'This is ' : 'A Nice day'}",
key, {
iv : CryptoJS.enc.Hex.parse(iv),
mode : CryptoJS.mode.CBC,
padding : CryptoJS.pad.Pkcs7
});
var encryptedInHex = encrypted.ciphertext
.toString(CryptoJS.enc.Hex); // converting the encrypted data in Hexadecimal
document.getElementById("thisDiv").innerHTML = encryptedInHex.toUpperCase();
return encryptedInHex.toUpperCase(); // returning the hashed encrypted data.
};
I have also developed a fiddle for it here -
http://jsfiddle.net/akki166786/1c24d1mj/3/
This is symmetric key cryptography being used here.
when I am trying to decrypt it on server side I am getting,
javax.Crypto.BadPaddingException : Given final block not properly padded exception,
Can there be a problem from Client side as well?
I need a server side(written in java) code to decrypt the out put of the function which i Have written in fiddle.
Thanks for your help.
You'll find the Java code to decrypt the ciphertext below. A couple of notes first:
server-side code uses commons-codec for hex decoder
in client-side code, replace key parameter to encrypt function with CryptoJS.enc.Hex.parse(key)
That should be it. And now the code ...
public class Decryptor {
private static final String IV = "3ad5485e60a4fecde36fa49ff63817dc";
private static final String KEY = "0a948a068f5d4d8b9cc45df90b58d382d2b916c25822b6f74ea96fe6823132f4";
private final static String CIPHERTEXT = "4E6B9EC6B5A0614BF9D69C5B0B5AE03D27484F2DB9D450E50EE623E80B8E34F5";
public static final void main(String[] args) throws DecoderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
SecretKeySpec sks = new SecretKeySpec(Hex.decodeHex(KEY.toCharArray()), "AES");
IvParameterSpec iv = new IvParameterSpec(Hex.decodeHex(IV.toCharArray()));
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, sks, iv);
byte[] plain = c.doFinal(Hex.decodeHex(CIPHERTEXT.toCharArray()));
String plainText = new String(plain);
System.out.println(plainText);
}
}

Is this a safe way to use java to encrypt (using public private key) messages that might be sent across insecure networks

I am not an expert on this so I need to defer to an expert. Is the following example a reasonably secure way to encrypt and decrypt a message (of an unknown length) for transmission a potentially insecure network (i.e. Email, HTTP requests, or other means). By "reasonably secure" I mean, would prevent a casual, or semi determined third party from reading the message.
Encrypt a message with a random AES key, and protect AES key by encrypting it with a public key.
public static String encrypt(String data, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
// Create AES secret key
Cipher aes = Cipher.getInstance("AES");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256);
SecretKey key = kgen.generateKey();
SecretKeySpec aeskeySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Encrypt data with AES Secret key
aes.init(Cipher.ENCRYPT_MODE, aeskeySpec);
byte[] dataEncoded = aes.doFinal(data.getBytes());
// Encrypt the secret AES key with the public key
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] aesKeyEncoded = rsa.doFinal(key.getEncoded());
// Output both secret AES key and data
return
Base64.getEncoder().encodeToString(aesKeyEncoded) + "~" +
Base64.getEncoder().encodeToString(dataEncoded);
}
Decrypt the AES secret key, and then decrypt the message:
public static String decrypt(String data, PrivateKey privateKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
String[] parts = data.split("~");
// Decrypt AES secret key
byte[] encodedSecretKey = Base64.getDecoder().decode(parts[0]);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodedSecretKey = rsa.doFinal(encodedSecretKey);
SecretKeySpec key = new SecretKeySpec(decodedSecretKey, "AES");
// Decrypt message
Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.DECRYPT_MODE, key);
byte[] decodedData = aes.doFinal(Base64.getDecoder().decode(parts[1]));
return new String(decodedData);
}
Using the above methods:
public static void main(String args[]) throws NoSuchAlgorithmException, BadPaddingException, NoSuchPaddingException, IllegalBlockSizeException, InvalidKeyException {
// Generate public/private key
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
KeyPair kp = generator.generateKeyPair();
System.out.println(" Public key = " + Base64.getEncoder().encodeToString(kp.getPublic().getEncoded()));
System.out.println("Private key = " + Base64.getEncoder().encodeToString(kp.getPrivate().getEncoded()));
String mytext = "test message with some test data.";
String e = encrypt(mytext, kp.getPublic());
String d = decrypt(e, kp.getPrivate());
System.out.println(" text = " + mytext);
System.out.println("Decoded text = " + d);
}
As long as you can trust the RSA public key the general idea is OK. If you just send the public key to the other side, then it is not.
You also need to protect your ciphertext by adding integrity and authenticity. You can easily do this by switching to AES / GCM mode (which is only available in Java 8, or using Bouncy Castle). Currently you are using the unsafe AES / ECB mode of encryption.
You should try and use RSA with OAEP padding, instead of PKCS#1 v1.5 padding. In general, you should not rely on default character encodings (getBytes()) or cipher modes.
So in the end: no, that's not secure. Try and use TLS is you want to avoid the many pitfalls.

RSA Encryption / Decryption using Java

I am doing a simple program to encrypt/decrypt using RSA algorithm in Java. I create a cipher object as follows:
//Create a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");
I do the encryption by calling the encrypt function:
String cipher=encrypt(textByte, pair, rsaCipher);
System.out.println("The Encryption using RSA Algorithm : "+cipher);
And the decryption as:
//Decryption
String plain=decrypt(Base64.decodeBase64(cipher),pair, rsaCipher);
System.out.println("The Decryption using RSA Algorithm : "+plain);
When I display the output, the decryption output returns a long space before the original text:
However, when I edit the code for creating the Cipher object to be:
//Create a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA");
i.e, removed the operation mode and padding arguments, the problem get resolved and the output becomes:
Where is the problem. In the first case (when the space appears), I specified NoPadding? Why the spaces appears in the decrypted message ? Even if I used padding, I expect this should not happen.
EDIT:
This is the encrypt and decrypt methods:
public static String encrypt(byte[] textBytes, KeyPair pair, Cipher rsaCipher) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
//get the public key
PublicKey pk=pair.getPublic();
//Initialize the cipher for encryption. Use the public key.
rsaCipher.init(Cipher.ENCRYPT_MODE, pk);
//Perform the encryption using doFinal
byte[] encByte = rsaCipher.doFinal(textBytes);
// converts to base64 for easier display.
byte[] base64Cipher = Base64.encodeBase64(encByte);
return new String(base64Cipher);
}//end encrypt
public static String decrypt(byte[] cipherBytes, KeyPair pair, Cipher rsaCipher) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
{
//get the public key
PrivateKey pvk=pair.getPrivate();
//Create a Cipher object
//Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");
//Initialize the cipher for encryption. Use the public key.
rsaCipher.init(Cipher.DECRYPT_MODE, pvk);
//Perform the encryption using doFinal
byte[] decByte = rsaCipher.doFinal(cipherBytes);
return new String(decByte);
}//end decrypt
Your problem is indeed with the padding. Some kind of padding, either PKCS#1 1.5 or OAEP padding in practice, is required for secure RSA functionality. Furthermore, it is required to find the start and end of the encrypted plain text.
The modular exponentiation of RSA is performed using large integers. The results of these operations are then represented as octet strings. These octet strings are basically big endian, unsigned, fixed length representation of an integer. These integers are left padded with 00 valued bytes (this is called the I2OS primitive in the RSA standard). So what you are seeing is the result of the modular exponentiation, with the 00 padding still in place.
Long story short, always use a padding scheme. Nowadays, OAEP would be preferable. Use it together with hybrid encryption scheme, or use a higher level container format such as CMS or PGP.
//This is a complete encryption and decryption module using
//Algorithm: JWEAlgorithm.RSA_OAEP_256
//Encryption Method: A128CBC_HS256
public static String encrypt(String text) throws Exception {
// Set the plain text
Payload payload = new Payload(text);
// Create the header
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128CBC_HS256);
// Create the JWE object and encrypt it
JWEObject jweObject = new JWEObject(header, payload);
jweObject.encrypt(new RSAEncrypter(getPublicKey()));
// Serialise to compact JOSE form...
String jweString = jweObject.serialize();
LOG.info("Generated Encrypted Key : {}", jweString);
return jweString;
}
public static String decrypt(String text) throws Exception {
// Parse into JWE object...
JWEObject jweObject = JWEObject.parse(text);
jweObject.decrypt(new RSADecrypter(getPrivateKey()));
// Get the plain text
Payload payload = jweObject.getPayload();
System.out.println(payload.toString());
return payload.toString();
}
private static RSAPublicKey getPublicKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/pub.der";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPublicKey) kf.generatePublic(spec);
}
private static RSAPrivateKey getPrivateKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/private.pkcs8";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec1 = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(spec1);
}

Categories

Resources