So I am trying to connect to a website service (Steam) which requires the program to have a Base64 encrypted key which contains the password of the account which I am trying to logon to. I have the RSA key but am having a little difficulty encrypting it with the password. Here is my code:
if(jsonObject.getBoolean("success")){
String timeStamp = jsonObject.getString("timestamp");
String publickey_mod = jsonObject.getString("publickey_mod");
String keyexp = jsonObject.getString("publickey_exp");
String modulus_preHex = new BigInteger(1, publickey_mod.getBytes("UTF-8")).toString(16);
String exponent_preHex = new BigInteger(1, keyexp.getBytes("UTF-8")).toString(16);
BigInteger modulus_post = new BigInteger(modulus_preHex);
BigInteger exponent_post = new BigInteger(exponent_preHex);
PublicKey key = getEncrpytedKey(modulus_post, exponent_post);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] passArray = pass.getBytes("UTF-8");
String encode = Base64.encodeBase64(cipher.doFinal(passArray)).toString();
System.out.println(encode + " - encode");
}
This takes place after a successful POST to the URL where I retrieve the RSAKEY (publickey_mod) and when it expires (keyexp). After ciphering the PublicKey I Base64 encode the string.
public PublicKey getEncrpytedKey(BigInteger m, BigInteger e) throws Exception{
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);
return pubKey;
}
^^^The method seen being used to get the PublicKey.
When I run this program, the 'encode' String does not change, even when I change the password that is it running with, it is currently:
String encode = [B#32d992b2
When the RSAKey is AB8AA7A6C2513D3CFFCA147F64E7B9C5400CCE5B36D3F881387659A51701D76BB383C1E50F52EF8DE28AD20F6257B8E0013720E4704F6ABDBCC19E477BD58C534A93895408157BE7874152A0153499773BC3AE4E536543A74341D58F7EA9CF74487C9560625BACE2315FB0983BD9E78BD812AE7DDC356427CC7626DCB8D25900658FE5E309D35BF794445F6EF9279882699E1CCBEE2F4193BA2634D0F7B24CFD7EC6A2AA8B35A48969C43DBF759095FCBD087C4D2D8123F598A4F43AECE0CE4E8FA536ECD84782092A329DF96FF88A25677AA386669B4ACAA41D7B6AAD456307357C70E25FD027970F5F0CF952894A065A37BA1BD2222A3AC2CD8FB69632560F
Note that I am not really well versed with encryption, as this is one of my first times trying it out.
Thanks for any help,
Tim
NOTE: I am using org.apache.commons.codec.binary.Base64; for Base64.
Base64.encodeBase64 returns a byte array so what you want is
encode = new String (Base64.encodeBase64(cipher.doFinal(passArray));
Related
I am trying to encript a string in an Android app with a public key that I get from my php server.
php server code:
$res = openssl_pkey_new();
openssl_pkey_export($res, $privKey);
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
echo $pubKey;
The server create correctly the key. In my app I store the public key in "response" variable. This var is like:
response = "-----BEGIN PUBLIC KEY-----MIIB... etc ...wIDAQAB-----END PUBLIC KEY-----"
Android code:
String pass = "password";
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(response.trim().getBytes(), Base64.DEFAULT));
Key publicKey = keyFac.generatePublic(keySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherText = cipher.doFinal(pass.getBytes());
strEncryInfoData = new String(Base64.encode(cipherText,Base64.DEFAULT));
} catch (Exception e){
}
When I run the application, appear this error:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
I don't know why the code doesn't encript the password. Any idea? It is about the encode type?
Thanks.
Try removing the BEGIN header and END footer and all the newlines, then you get the Base64 encoded DER which you need to decode and pass to the X509EncodedKeySpec constructor. Here's some code:
response = response.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
response = response.replace("\n", "").replace("\r", "");
// Use Base64.NO_WRAP because: https://stackoverflow.com/questions/32935783/java-different-results-when-decoding-base64-string-with-java-util-base64-vs-and/32935972
byte[] responseBytes = Base64.decode(response, Base64.NO_WRAP);
KeySpec keySpec = new X509EncodedKeySpec(responseBytes);
I'm developing an android app and getting this public key from the server of the company that I work with:
PFJTQUtleVZhbHVlPjxNb2R1bHVzPnZOcFhkRWVOTU5pZDhuOTlUekRGMVo4MDNvTEdRSzlqWnNFODlDd2tiS29GV0tGZmt2QTZKODBNWHhPZnhqbFZIYU8vYWM4YUpMc1AxWVR1RFNHVis3VExQL0puVVpyNlJQQTdpbFlmMitVWExiS0U2ZW1RYzBKdXlOaVArL0FTMGZmKzYwSnZQekhYeEdQQnVIbWtTcmRqdEtFV0JCZXJzWWNuQVJyT2ZSYz08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjwvUlNBS2V5VmFsdWU+
the server is windows with IIS 7.5.
base64decode it give me this XML
<RSAKeyValue><Modulus>vNpXdEeNMNid8n99TzDF1Z803oLGQK9jZsE89CwkbKoFWKFfkvA6J80MXxOfxjlVHaO/ac8aJLsP1YTuDSGV+7TLP/JnUZr6RPA7ilYf2+UXLbKE6emQc0JuyNiP+/AS0ff+60JvPzHXxGPBuHmkSrdjtKEWBBersYcnARrOfRc=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
Extracting the Modulu and the Exponent from this xml, base64decode them and making the spec for the public key object:
PublicKey pbKey = null;
XMLParser parser = new XMLParser();
Document doc = parser.getDomElement(publicKeyString);
Element rsakeyvalue = doc.getDocumentElement();
String modulusBase64 = parser.getValue(rsakeyvalue, "Modulus");
byte[] modulus = Base64.decode(modulusBase64, 0);
String exponentBase64 = parser.getValue(rsakeyvalue, "Exponent");
byte[] exponent = Base64.decode(exponentBase64, 0);
BigInteger modBigInteger = new BigInteger(1,modulus);
BigInteger exBigInteger = new BigInteger(1,exponent);
RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);
try {
KeyFactory factory = KeyFactory.getInstance("RSA");
pbKey = factory.generatePublic(spec);
} catch (Exception e) {
e.printStackTrace();
}
Creating the cipher and adding the plain text to encrypt with:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
byte[] keyBytes = Base64.decode(this.publicKey, 0);
/* the strToPublicKey is the previews code block */
PublicKey publickey = strToPublicKey(new String(keyBytes));
cipher.init( Cipher.ENCRYPT_MODE , publickey );
// Base 64 encode removed.
//byte[] encryptedBytes = Base64.encode( cipher.doFinal(plainText.getBytes()), 0 );
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
everything here is working but the server don't accept it,
I'm base64 encode it and sending it as byte array.
the server admin saying it's too long, supposed to be 128 bit but it's 174 bit...
what I'm doing wrong?
How its called this key? RSA XML is correct? or it's got other name?
I can also get PEM string from the server if someone have an idea how to do it with it.
Edit:
i forgot to put here an important part, i'm sending the data to the server as byte array, this is how i make the string of it:
public static int unsignedToBytes(byte b) {
return b & 0xFF;
}
StringBuilder byteArrayString = new StringBuilder();
int bytesLength = bytes.length;
int bytesCounter = 0;
for (byte aByte : bytes) {
bytesCounter++;
byteArrayString.append(unsignedToBytes(aByte));
if(bytesCounter < bytesLength){
byteArrayString.append(",");
}
}
SOLVED! - the solution:
the bytes that the string builder used are signed so i use this function unsignedToBytes() to make them unsigned and i removed the base64Encription on the encrypt() function.
this was the problem, i hope it will help anyone else.
Your variable naming is bad. encryptedBytes contains not the encrypted bytes but the encrypted bytes in base64 encoding.
This is the reason you do not get the expected result.
The length of the encrypted data before applying Base64 encoding is 128 byte.
Afterwards the length is 172 bytes.
I have RSA Public-Key on server side in below format:
<string xmlns="http://www.cherripik.com/">
<RSAKeyValue><Modulus>abc</Modulus><Exponent>abc</Exponent></RSAKeyValue>
</string>
I have tried almost all possible ways but could not able to encrypt string with this public key on android side. Could anyone give me an example in which i will encrypt any of the string like "abc" with this public key and also decrypt that encrypted key to back to "abc". It will be very helpful to me.
Advance Thanks.
Below are the ways which i have used but no success. It gave some value but it is not correct.
public String encrypt(String message, String Modulus, String Exponent) {
String outputEncrypted = "";
try {
byte[] modulusBytes = Base64Coder.decode(Modulus);
byte[] exponentBytes = Base64Coder.decode(Exponent);
BigInteger modulus = new BigInteger(modulusBytes );
BigInteger exponent = new BigInteger(exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = new String("abc").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal( plainBytes );
String encryptedString = new String(Base64Coder.encode(cipherData));
Log.i(this.getClass().getSimpleName(), "encryptedString : "+encryptedString);
} catch (Exception e) {
// TODO: handle exception
}
return outputEncrypted;
}
One more thing when I create encrypted string with above method. It will give 346 characters encrypted string. But on my server, I have only encrypt and decrypt method. On server encrypt method, it will producing 344 character. Lastly when I put my encrypted string to server method to verify that my encrypted string is correct. Server throw this error.
<string xmlns="http://www.Myserver.com/">Error occurred while decoding OAEP padding.</string>
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);
}
I use the following code to encrypt some data and I want to move the decryption code to a server so need to send the cipherData (which is a byte [] array ) to my server over REST
BigInteger modulus = new BigInteger("blah");
BigInteger exponent = new BigInteger("blah");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory encryptfact = KeyFactory.getInstance("RSA");
PublicKey pubKey = encryptfact.generatePublic(keySpec);
String dataToEncrypt = "Hello World";
/**
* Encrypt data
*/
Cipher encrypt = Cipher.getInstance("RSA");
encrypt.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherData = encrypt.doFinal(dataToEncrypt.getBytes());
System.out.println("cipherData: " + new String(cipherData));
/**
* Decrypt data
*/
BigInteger privatemodulus = new BigInteger("blah");
BigInteger privateexponent = new BigInteger("blah");
RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(privatemodulus, privateexponent);
PrivateKey privateKey = encryptfact.generatePrivate(privateKeySpec);
Cipher decrypt = Cipher.getInstance("RSA");
decrypt.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decData = decrypt.doFinal(cipherData);
System.out.println(new String(decData));
This works fine.
I was hoping I could just create a new String with the cipherData as a parm
When I try this with the above example I get the following error
byte[] decData = decrypt.doFinal(new String(cipherData).getBytes());
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:308)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:255)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at com.test.EncryptTest.main(EncryptTest.java:52)
Any ideas?
I was hoping I could just create a new String with the cipherData as a parm
No. cipherData is arbitrary binary data. It's not encoded text, which is what the various String constructors expect. (As an aside, you should almost never call the String.getBytes() or new String(byte[]) which don't specify an encoding. Always specify an appropriate encoding, which will depend on the situation.)
Either transmit the data as binary data instead of going through text at all, or use Base64 to safely encode the binary data as text first, then decode it from Base64 to binary again later before decrypting. There's a public domain Base64 encoder which is easy to use.