RSA Encryption using public key in Android for a PHP server - java

I am trying to encrypt a string using the RSA public key encryption. I have a public key which is:
-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7yyFgPV6Fk8JWngl3MIz\n1U2DMwKPNCRkeQ79g/qwKL0LP5aVkQUUxqYhX0mmQMWnT5t+7Hoc6UUxyjGSkI9o\nv0Q3oTSVtByIQDdySXZWihzjVjn3h98chevZuNNkJ4GNADHj5K/7LOWLpKSQJ2Hj\nIFdVrcKSjy4kiP/UMHgsfq0GWQAtGv8ebcybWuEf8rzTMndxmI96Nmz5PgPK7K75\nXbMgJlOMoMlXDsgmghpGzH8p10r/qHGlYi/COa4PZ7Pvbveg1BoH5LPy/8mLZ+Oa\n9owl10yBIoh9/H5KnijZ0Uq8MH0QdgQXekLC9sRh3uGTe69IQrGXzSv7tHe5fgv6\nnwIDAQAB\n-----END
PUBLIC KEY-----
I am using following code to encrypt it :
private String generateRSAEncryptedText(String publicKey) {
String baseCredentials = email + "---" + password;
try {
return encryptRSA(context, baseCredentials, publicKey);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private String getPublicKeyStringFromPemFormat(String PEMString, boolean isFilePath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
BufferedReader pemReader = null;
if (isFilePath) {
pemReader = new BufferedReader(new InputStreamReader(new FileInputStream(PEMString)));
} else {
pemReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(PEMString.getBytes("UTF-8"))));
}
StringBuffer content = new StringBuffer();
String line = null;
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----BEGIN PUBLIC KEY-----") != -1) {
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----END PUBLIC KEY") != -1) {
break;
}
content.append(line.trim());
}
break;
}
}
if (line == null) {
throw new IOException("PUBLIC KEY" + " not found");
}
Log.i("PUBLIC KEY: ", "PEM content = : " + content.toString());
return content.toString();
}
public String encryptRSA(Context mContext, String message, String publicKeyString) throws Exception {
String keyString = getPublicKeyStringFromPemFormat(publicKeyString, false);
// converts the String to a PublicKey instance
byte[] keyBytes = Base64.decode(keyString.getBytes("utf-8"), Base64.NO_WRAP);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(spec);
// decrypts the message
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
dectyptedText = cipher.doFinal(Base64.decode(message.getBytes("utf-8"), Base64.NO_WRAP));
return Base64.encodeToString(dectyptedText, Base64.NO_WRAP);
}
Using this, i am able to generate an encrypted text but the PHP server is not able to decrypt my encrypted text.
Server is working correctly as i am able to the whole exercise using a python script but it is not working for android.
Any idea what i might be doing wrong.. all help is appreciated.
Some other pointer:
I get the public key as a string from the server and it can be changed at any time so cant save it in a cert file.
I am using android.util.Base64 class for encoding.
Let me know if you need any other information.
Working Python Code:
import requests
import base64
def try_api():
API_ENDPOINT = 'https://example.com/login.php'
PUBLIC_KEY = None
###get_pulic_key###
payload = {'requestType':'getPubkey'}
r = requests.post(API_ENDPOINT, data=payload, verify=False)
mycookies = dict (r.cookies)
#print mycookies
res = r.json()
PUBLIC_KEY = res[1]
print "PublicKey\n", PUBLIC_KEY
###get_token###
credential = prepare_credentials('abc#gmail.com', 'abctest', PUBLIC_KEY)
print "Credential\n", credential
cred_payload = {'requestType':'credentialLogin', 'credential':credential}
r = requests.post(API_ENDPOINT, data=cred_payload, cookies=mycookies, verify=False)
#print r.text
status, token =r.json()
def prepare_credentials(username, password, public_key):
"""
Given username and password prepare the credentials
"""
basecred = "%s---%s" % (username, password)
#print "basecred\n", basecred
basecred64 = base64.b64encode(basecred)
#print "basecred64\n", basecred64
basecred64encrypted64 = encrypt_RSA(public_key, basecred64)
#print "basecred64encrypted64\n", basecred64encrypted64
return basecred64encrypted64
def encrypt_RSA(public_key, message):
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
rsakey = RSA.importKey(public_key)
rsakey = PKCS1_v1_5.new(rsakey)
encrypted = rsakey.encrypt(message)
return base64.b64encode(encrypted)
if __name__=='__main__':
try_api()

Related

Encrypt/Decryption RSA communication Java-PHP

I'm trying to implement a client-server communication encrypted with RSA.
I generate private and public key pairs in java then I pass some inputs to a PHP script through GET parameters. If there is only the key set as GET parameter the script encrypts a phrase that will be decrypted from the client (by java) otherwise if it's set a message (the m parameters see the PHP script) it should be able to decrypt it.
I coded the following:
java:
public static void main(String[] args) throws Exception {
// Generate public and private keys using RSA
Map<String, Object> keys = getRSAKeys();
PrivateKey privateKey = (PrivateKey) keys.get("private");
PublicKey publicKey = (PublicKey) keys.get("public");
StringBuilder keypublic = new StringBuilder();
keypublic.append("-----BEGIN PUBLIC KEY-----\n");
keypublic.append(Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) + "\n");
keypublic.append("-----END PUBLIC KEY-----\n");
StringBuilder keyprivate = new StringBuilder();
keypublic.append("-----BEGIN PRIVATE KEY-----\n");
keypublic.append(Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()) + "\n");
keypublic.append("-----END PRIVATE KEY-----\n");
String keyEncodedPublic = Base64.getEncoder().encodeToString(keypublic.toString().getBytes());
String keyEncodedPrivate = Base64.getEncoder().encodeToString(keypublic.toString().getBytes());
String signature = sign("MyEncryptedInternalString", privateKey);
//Offuscare la MyEncryptedInternalString in qualche modo
System.out.println("key: \n" + Base64.getEncoder().encodeToString(signature.getBytes()) + ":" +
keyEncodedPublic);
System.out.println("\n");
System.out.println("Crypted: \n" + Base64.getEncoder().encodeToString(encryptMessage("hello", privateKey).getBytes()));
//System.out.println("Verified? " + verify("test",signature,publicKey));
while(true) {
Scanner out = new Scanner(System.in);
System.out.print("Insert text: ");
String enc = out.nextLine();
String descryptedText = decryptMessage(enc, privateKey);
System.out.println("Decrypted: " + descryptedText);
System.out.println();
}
}
public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(publicKey);
publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return publicSignature.verify(signatureBytes);
}
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signature = privateSignature.sign();
return Base64.getEncoder().encodeToString(signature);
}
// Get RSA keys. Uses key size of 2048.
private static Map<String,Object> getRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Map<String, Object> keys = new HashMap<String,Object>();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
// Decrypt using RSA public key
private static String decryptMessage(String encryptedText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)));
}
// Encrypt using RSA private key
private static String encryptMessage(String plainText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes()));
}
And the PHP script:
<?php
function RSAEncryptMessage($string, $key){
$data = "MyEncryptedInternalString";
$parts = explode(":",$key);
$signature = base64_decode($parts[0]);
$pubkeyid = $parts[1];
$ok = openssl_verify($data, base64_decode($signature), base64_decode($pubkeyid), "sha256WithRSAEncryption");
if ($ok == 1){
$pubkeyid = base64_decode($pubkeyid);
openssl_public_encrypt($string, $crypted, $pubkeyid);
}
return base64_encode($crypted);
}
function RSADecryptMessage($string, $key){
$data = "MyEncryptedInternalString";
$parts = explode(":",$key);
$signature = base64_decode($parts[0]);
$pubkeyid = $parts[1];
//$ok = openssl_verify($data, base64_decode($signature), base64_decode($pubkeyid), "sha256WithRSAEncryption");
$pubkeyid = base64_decode($pubkeyid);
openssl_private_decrypt(base64_decode($string), $decrypted, $pubkeyid);
echo "Decrypted text: ". $decrypted;
return $decrypted;
}
$mex = "hello";
//echo RSAEncryptMessage($m,$_GET['key']);
if(isset($_GET['m'])){
echo RSADecryptMessage($_GET['m'],$_GET['key']);
}else{
echo RSAEncryptMessage($mex,$_GET['key']);
}
?>
So when I try to encrypt the content by server-side and then decrypting it in Java it works correctly. I'm assuming to use the generated public key to encrypt the content (sending it to the server) and decrypting the content by using the stored private key on the client too.
The question:
I'm not able to do the contrary: encrypt by client-side and decrypting by server-side using the PHP function RSADecryptMessage. When passing the parameters it doesn't work and writes nothing.
EDIT:
Since I need a communication both sides encrypted to avoid MITM attacks, for example, I use PUBLICKEY generated by the client, then I send the key to the server. So I encrypt my message from the server using the same public key. In this way, from the client, I'm able to decrypt the response by the server using the private key. The best way would be which server generates both public and private key pairs but unfortunately, Java is not able to decrypt a message if the key pair is generated and shared by PHP and that's why I allow the client to generate the key pair. This works. Now I need to do the contrary too, with the same public key sent previously to the server I need to decrypt a message encrypted by the client this time (in the image you can see the string "crypted"). Unfortunately, this seems not to work, because when I call the PHP script, the content is not decrypted.
Solved.
When you want to send an encrypted message from PHP to Java, PHP will
have to encrypt it with the public key from Java. Then Java will
decrypt it with his private key. When you want to send an encrypted
message from Java to PHP, Java will have to encrypt it the public key
from PHP. Then PHP will decrypt it with his private key. Both parties
generate their own key-pair and send the public-key to the other
party. Btw.: Signing does not encrypt the message, the message will
still be visible/readable.
Comment of #Progman
Code.
java:
public static void main(String[] args) throws Exception {
// Generate public and private keys using RSA
Map<String, Object> keys = getRSAKeys();
PrivateKey privateKey = (PrivateKey) keys.get("private");
PublicKey publicKey = (PublicKey) keys.get("public");
StringBuilder keypublic = new StringBuilder();
keypublic.append("-----BEGIN PUBLIC KEY-----\n");
keypublic.append(Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) + "\n");
keypublic.append("-----END PUBLIC KEY-----\n");
String keyEncodedPublic = Base64.getEncoder().encodeToString(keypublic.toString().getBytes());
String signature = sign("MyEncryptedInternalString", privateKey);
System.out.println("key: \n" + Base64.getEncoder().encodeToString(signature.getBytes()) + ":" + keyEncodedPublic);
System.out.println("\n");
while(true) {
Scanner out = new Scanner(System.in);
System.out.print("Insert text: ");
String enc = out.nextLine();
String descryptedText = decryptMessage(enc, privateKey);
System.out.println("Decrypted: " + descryptedText);
System.out.println();
Scanner out2 = new Scanner(System.in);
System.out.print("Insert text2: ");
String enc2 = out2.nextLine();
byte[] decodedBytesKey = Base64.getDecoder().decode(enc2);
//String content = encryptMessage("message from the client", new String(decodedBytes));
String publicKeyPEM = new String(decodedBytesKey)
.replace("-----BEGIN PUBLIC KEY-----", "")
.replaceAll(System.lineSeparator(), "")
.replace("-----END PUBLIC KEY-----", "");
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
PublicKey p = keyFactory.generatePublic(keySpec);
System.out.println(encryptMessage("Message from client",p));
}
}
public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(publicKey);
publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return publicSignature.verify(signatureBytes);
}
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signature = privateSignature.sign();
return Base64.getEncoder().encodeToString(signature);
}
// Get RSA keys. Uses key size of 2048.
private static Map<String,Object> getRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Map<String, Object> keys = new HashMap<String,Object>();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
private static String decryptMessage(String encryptedText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)));
}
private static String encryptMessage(String plainText, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return URLEncoder.encode(Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes())));
}
PHP:
<?php
include ("utils/RSA.php");
$config = array(
"digest_alg" => "sha512",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $privKey);
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
echo "PubKey: " . base64_encode($pubKey) ."<br>";
echo "PrivateKey: " . base64_encode($privKey) . "<br>";
//echo "Encrypted: " . base64_encode($encrypted);
function RSAEncryptMessage($string, $key){
$data = "MyEncryptedInternalString";
$parts = explode(":",$key);
$signature = base64_decode($parts[0]);
$pubkeyid = $parts[1];
$ok = openssl_verify($data, base64_decode($signature), base64_decode($pubkeyid), "sha256WithRSAEncryption");
if ($ok == 1){
$pubkeyid = base64_decode($pubkeyid);
openssl_public_encrypt($string, $crypted, $pubkeyid);
}
return base64_encode($crypted);
}
function RSADecryptMessage($encrypted, $key){
//todo
openssl_private_decrypt(base64_decode($encrypted), $decrypted, base64_decode($key));
echo "Decrypted: " . $decrypted;
}
if((isset($_GET['providemessage']))){
echo RSADecryptMessage($_GET['m'],$_GET['key']);
}else if (isset($_GET['getmessage'])){
$mex = "Message from server";
echo RSAEncryptMessage($mex,$_GET['key']);
}
?>
Screenshots:

How to decrypt PKCS7 bouncycastle in java

I have message encrypt by PKCS7 Asymmetric encryption method. inside have xml
I have tried to decrypt by this below
public static byte[] decryptData(byte[] encryptedData, PrivateKey decryptionKey) throws CMSException {
byte[] decryptedData = null;
if (null != encryptedData && null != decryptionKey) {
CMSEnvelopedData envelopedData = new CMSEnvelopedData(encryptedData);
Collection<RecipientInformation> recipients = envelopedData.getRecipientInfos().getRecipients();
KeyTransRecipientInformation recipientInfo = (KeyTransRecipientInformation) recipients.iterator().next();
JceKeyTransRecipient recipient = new JceKeyTransEnvelopedRecipient(decryptionKey);
return recipientInfo.getContent(recipient);
}
return decryptedData;
}
its error malformed content when
CMSEnvelopedData envelopedData = new CMSEnvelopedData(encryptedData);

How to fix "java.security.InvalidKeyException: Unsupported key algorithm: EC. Only RSA supported" while using Keystore in api 18

I need to store sensitive data in local storage with an API 18 , i choose to use the Keystore. I try several solution but none worked.
I try to make my RSAPrivateKey in PrivateKey without cast but it don't work.
I also try to use other crypting algorithm but i never success to make them work in API 18
public String decryptString(String alias, String encryptedText) {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
String decryptedText = "";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKey);
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(encryptedText, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
decryptedText = new String(bytes, 0, bytes.length, "UTF-8");
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return decryptedText;
}
public String encryptString(String alias, String initialText) {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
String encryptedText = "";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
// Encrypt the text
if(initialText.isEmpty()) {
Log.e(TAG, "initialText is Empty");
return "";
}
Cipher input = Cipher.getInstance("RSA/ECB/PKCS1Padding");
input.init(Cipher.ENCRYPT_MODE, publicKey);//Need RSA private or public key
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, input);
cipherOutputStream.write(initialText.getBytes("UTF-8"));
cipherOutputStream.close();
byte [] vals = outputStream.toByteArray();
encryptedText = Base64.encodeToString(vals, Base64.DEFAULT);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return encryptedText;
}
Here is the erot i get. I would like to success to keep my data in a secure place
java.security.InvalidKeyException: Unsupported key algorithm: EC. Only RSA supported
at com.cryptor.Cryptor.encryptString(Cryptor.java:108)
I don't see where/when you generate your RSA key. On my side, I have done the following steps :
Create/retrieve the Keystore
Generate RSA keys with a KeyPairGenerator (be careful : different methods since Android M)
val generator = KeyPairGenerator.getInstance(ALGORITHM, CryptoConstants.ANDROID_KEY_STORE)
Here, ALGORITHM="RSA" and not "RSA/ECB/PKCS1Padding" and CryptoConstants.ANDROID_KEY_STORE = "AndroidKeyStore" (for example)
Save keys in the Keystore
Encrypt with the public key
Decrypt with the private key
With these steps, my encryption methods are
fun encrypt(publicKey: PublicKey, rawText: ByteArray): String {
try {
val cipher = CipherUtil.getStandardCipherInstance(TRANSFORMATION) // TRANSFORMATION = "RSA/ECB/PKCS1Padding"
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val bytes = cipher.doFinal(rawText)
return Base64.encodeToString(bytes, BASE64_SETTINGS) // BASE64_SETTINGS = Base64.NO_WRAP
} catch (e: GeneralSecurityException) {
throw SecurityException(e)
}
}
fun decrypt(privateKey: PrivateKey, base64CipherBytes: ByteArray): ByteArray {
try {
val cipher = CipherUtil.getStandardCipherInstance(TRANSFORMATION) // TRANSFORMATION = "RSA/ECB/PKCS1Padding"
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val encryptedData = Base64.decode(base64CipherBytes, BASE64_SETTINGS) // BASE64_SETTINGS
return cipher.doFinal(encryptedData)
} catch (e: GeneralSecurityException) {
throw SecurityException(e)
}
}
Btw, you can bypass the Base64 encoding if you don't need it.

Digital Signature in java / android (RSA keys)

I would like to generate a digital signature in my java/android project with a private key(RSA) stored in DB.
My 2 keys was generated with the below code (project is in production and I cannot change it):
// Get keys pair (RSA)
KeyPair rsaKyePair = createKeyPair();
// Get private/ public keys and store them in DB
String pri = getPrivateKeyBase64Str(rsaKyePair);
String pub = getPublicKeyBase64Str(rsaKyePair));
public static KeyPair createKeyPair() {
KeyPair keyPair = null;
try {
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
keygen.initialize(KEY_LENGTH);
keyPair = keygen.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
return keyPair;
}
public static String getPrivateKeyBase64Str(KeyPair keyPair){
if (keyPair == null) return null;
return getBase64StrFromByte(keyPair.getPrivate().getEncoded());
}
public static String getPublicKeyBase64Str(KeyPair keyPair){
if (keyPair == null) return null;
return getBase64StrFromByte(keyPair.getPublic().getEncoded());
}
public static String getBase64StrFromByte(byte[] key){
if (key == null || key.length == 0) return null;
return new String(Base64.encode(key));
}
Based on different sites (here and here), I'll try to write code for generate a signature:
String mySignature = getDigitalSignature("my_string_", "my_private_string" );
/*
* Generated a signed String
* #param text : string to sign
* #param strPrivateKey : private key (String format)
*/
public String getDigitalSignature(String text, String strPrivateKey) {
try {
// Get private key from String
PrivateKey pk = loadPrivateKey(strPrivateKey);
// text to bytes
byte[] data = text.getBytes("UTF8");
// signature
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(pk);
sig.update(data);
byte[] signatureBytes = sig.sign();
return javax.xml.bind.DatatypeConverter.printBase64Binary(signatureBytes);
}catch(Exception e){
return null;
}
}
private PrivateKey loadPrivateKey(String key64) throws GeneralSecurityException {
byte[] clear = Base64.decode(key64, Base64.DEFAULT);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clear);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey priv = fact.generatePrivate(keySpec);
Arrays.fill(clear, (byte) 0);
return priv;
}
For verify the signature, I use this code in my java API :
/*
* Verify signature of a string
* #param signature : signature
* #param origina: original string to verify
* #param publicKey: user public key
*/
public static boolean verfiySignature(String signature, String original, String publicKey){
try{
// Get private key from String
PublicKey pk = loadPublicKey(publicKey);
// text to bytes
byte[] originalBytes = original.getBytes("UTF8");
//signature to bytes
//byte[] signatureBytes = signature.getBytes("UTF8");
byte[] signatureBytes =javax.xml.bind.DatatypeConverter.parseBase64Binary(signature);
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initVerify(pk);
sig.update(originalBytes);
return sig.verify(signatureBytes);
}catch(Exception e){
e.printStackTrace();
Logger log = Logger.getLogger(RsaCipher.class);
log.error("error for signature:" + e.getMessage());
return false;
}
}
/*
* Generate a PublicKey object from a string
* # key64 : public key in string format (BASE 64)
*/
private static PublicKey loadPublicKey(String key64) throws GeneralSecurityException {
byte[] data = javax.xml.bind.DatatypeConverter.parseBase64Binary(key64);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
I've run this code with real data, but the "verifySignature" always returns "False".
I am a newbie in Encryption world, forgive me for my dirty code.
--- EDIT
I got an exception when the verify method is called:
java.security.SignatureException: Signature encoding error
When signing you returned your signature base64-encoded:
return Base64.encodeToString(signatureBytes, Base64.DEFAULT);
Thus, when verifying you have to base64-decode the signature string. But what you do is:
byte[] signatureBytes = signature.getBytes("UTF8");
So the signatureBytes you try to verify are completely different from the signatureBytes you had as a result of signing.
You sign using
Signature sig = Signature.getInstance("RSA");
But You verify using
Signature sig = Signature.getInstance("MD5WithRSA");
Obviously you should use the same algorithm in both cases.

Convert CRYPT_RSA_PUBLIC_FORMAT_PKCS1 from php to RSA Public key in Java

I'm trying to send a RSA Public Key from PHP to Java(Android). My PHP code looks something like this:
function __construct() {
$rsa = new Crypt_RSA();
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
// Code to Generate the public key and private key
$keys = $rsa->createKey(1024);
extract($keys);
// Base 64 encode Public and Private key
$this->rsa = $rsa;
$this->keys = $keys;
$this->publicKeyBase = base64_encode($publickey);
$this->privateKeyBase = base64_encode($privatekey);
}
And I send $this->publicKeyBase to my Android app. In Java I get the encoded string like this:
LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tDQpNSUdKQW9HQkFMQzluUkdhVGsybzlJTW5YVW0vWWRVMHMrTFplc09GUi9VYkU2K21hWDlwbGIwRW11RzZacHBMDQpoV2dRbUNBYmV6aW9ScHZNL0lVZHZWczZ6ZmFKaDRGTnFaRXo0cWd0V0ovaFpUU2RudlFIMlI3cWF0TEY0c0ZSDQpDbWNNVDZBdnYvdDJnR1liMW4vY1lhb01ralNOd1RFdTJBSU45djg0Skk2ZWhmOGNST0RMQWdNQkFBRT0NCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0=
And after decoding it, it looks something like this:
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBALC9nRGaTk2o9IMnXUm/YdU0s+LZesOFR/UbE6+maX9plb0EmuG6ZppL
hWgQmCAbezioRpvM/IUdvVs6zfaJh4FNqZEz4qgtWJ/hZTSdnvQH2R7qatLF4sFR
CmcMT6Avv/t2gGYb1n/cYaoMkjSNwTEu2AIN9v84JI6ehf8cRODLAgMBAAE=
-----END RSA PUBLIC KEY-----
How do I convert this to a pubic key that can be used in the Android App. Have seen lots of examples online but they aren't working and my java code(below) also seems not to work. Any possible solutions?
private static PublicKey getPublicKeyFromPemFormat(String PEMString, boolean isFilePath) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
BufferedReader pemReader = null;
if (isFilePath) {
pemReader = new BufferedReader(new InputStreamReader(
new FileInputStream(PEMString)));
} else {
pemReader = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(PEMString.getBytes("UTF-8"))));
}
StringBuffer content = new StringBuffer();
String line = null;
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----BEGIN RSA PUBLIC KEY-----") != -1) {
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----END RSA PUBLIC KEY-----") != -1) {
break;
}
content.append(line.trim());
}
break;
}
}
if (line == null) {
throw new IOException("PUBLIC KEY" + " not found");
}
Log.i("PUBLIC KEY: ", "PEM content = : " + content.toString());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decode(content.toString(), Base64.DEFAULT)));
}
new X509EncodedKeySpec(Base64.decode(content.toString(), Base64.DEFAULT)) throws an Invalid KeySpec Exception.
The documentation is a little confusing, but by looking at the source code for method _convertPublicKey($n, $e) starting at line 950 it appears that if $publicKeyFormat == PUBLIC_FORMAT_PKCS8 then the output format should be one that is compatible with Java's X509EncodedKeySpec class.

Categories