RSA encryption in PHP (openssl), Android / Java, JavaScript and Go

  • Tutorial
RSA is a public key encryption algorithm. Public key encryption is a very useful thing. RSA allows you to create two keys: public and private. Place the public key somewhere and encrypt it, and only the owner of the private key can decrypt it.

For example, we can create a web store on PhP that will accept orders with credit card information. The PHP store will encrypt credit card information with a public key. The PC store itself will not be able to decrypt these encrypted data. A good solution, a hacker unexpectedly breaks into a web store (written in PCP), and the cards are encrypted.

But how will the site owner get access to the maps? He needs to take the ciphertext, the private key and decrypt it. One can imagine that the private key will be stored on the phone, and the phone can pull the ciphertext from the admin panel via the QR code. But in different languages, the implementation of cryptography is slightly different, and my article is just about how to encrypt text in one programming language and decrypt in another language.

What are the differences?
- how keys are stored;
- how ciphertext is stored: binary form or base64 encoded;
- padding.

First of all, we need the keys. I suggest creating them using openssl

openssl genrsa -out private.pem 512
openssl rsa -in private.pem -out public.pem -outform PEM -pubout


To save screen space, I chose 512 bits, it is advisable to use 1024 or 2048 bits. For example, SSL gitgub.com uses 2048.

Also, the key size determines the maximum amount of data that you can encrypt, but taking into account the fact that we will use OPENSSL_PKCS1_PADDING (by default in the PCP), then 11 bytes should be subtracted from the key size and if With 512 bits, we can encrypt 53 bytes. It’s dangerous not to use padding if you don’t know why it is needed.

Now we have private.pem and public.pem. These keys are in text format, and it will be quite convenient to use them in the examples. I want each program to consist of one file, so it will be more visual.

private.pem
-----BEGIN RSA PRIVATE KEY-----
MIIBPQIBAAJBALqbHeRLCyOdykC5SDLqI49ArYGYG1mqaH9/GnWjGavZM02fos4l
c2w6tCchcUBNtJvGqKwhC5JEnx3RYoSX2ucCAwEAAQJBAKn6O+tFFDt4MtBsNcDz
GDsYDjQbCubNW+yvKbn4PJ0UZoEebwmvH1ouKaUuacJcsiQkKzTHleu4krYGUGO1
mEECIQD0dUhj71vb1rN1pmTOhQOGB9GN1mygcxaIFOWW8znLRwIhAMNqlfLijUs6
rY+h1pJa/3Fh1HTSOCCCCWA0NRFnMANhAiEAwddKGqxPO6goz26s2rHQlHQYr47K
vgPkZu2jDCo7trsCIQC/PSfRsnSkEqCX18GtKPCjfSH10WSsK5YRWAY3KcyLAQIh
AL70wdUu5jMm2ex5cZGkZLRB50yE6rBiHCd5W1WdTFoe
-----END RSA PRIVATE KEY-----


public.pem
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALqbHeRLCyOdykC5SDLqI49ArYGYG1mq
aH9/GnWjGavZM02fos4lc2w6tCchcUBNtJvGqKwhC5JEnx3RYoSX2ucCAwEAAQ==
-----END PUBLIC KEY-----


Let's start with php


encode.php


goo.gl/Xb7ayw

Get something like (you will get a new unique ciphertext every time you try to encrypt text):

JutBa0GLHzGrlygxwWr66cizw4W4za+DbzZweNM0iloCD7xEP9LclL013lcksJL5XhjW44U+oxpq
cX1ZSLhWuA==

decode.php


goo.gl/0CWTQ9

Now on go


package main
import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"strings"
)
func main() {
	b64 := `JutBa0GLHzGrlygxwWr66cizw4W4za+DbzZweNM0iloCD7xEP9LclL013lcksJL5XhjW44U+oxpq
cX1ZSLhWuA==
`
	b1, err := Base64Dec(b64)
	if err != nil {
		panic(err)
	}
	b2, err := RsaDecrypt(b1, privateKey)
	fmt.Println(string(b2), err)
	b1, err = RsaEncrypt([]byte("Go the best language"), publicKey)
	if err != nil {
		panic(err)
	}
	s1 := Base64Enc(b1)
	fmt.Println(s1)
	b1, err = Base64Dec(s1)
	b2, err = RsaDecrypt(b1, privateKey)
	fmt.Println(string(b2), err)
}
func Base64Enc(b1 []byte) string {
	s1 := base64.StdEncoding.EncodeToString(b1)
	s2 := ""
	var LEN int = 76
	for len(s1) > 76 {
		s2 = s2 + s1[:LEN] + "\n"
		s1 = s1[LEN:]
	}
	s2 = s2 + s1
	return s2
}
func Base64Dec(s1 string) ([]byte, error) {
	s1 = strings.Replace(s1, "\n", "", -1)
	s1 = strings.Replace(s1, "\r", "", -1)
	s1 = strings.Replace(s1, " ", "", -1)
	return base64.StdEncoding.DecodeString(s1)
}
func RsaDecrypt(ciphertext []byte, key []byte) ([]byte, error) {
	block, _ := pem.Decode(key)
	if block == nil {
		return nil, errors.New("private key error!")
	}
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}
func RsaEncrypt(origData []byte, key []byte) ([]byte, error) {
	block, _ := pem.Decode(key)
	if block == nil {
		return nil, errors.New("public key error")
	}
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	pub := pubInterface.(*rsa.PublicKey)
	return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALqbHeRLCyOdykC5SDLqI49ArYGYG1mq
aH9/GnWjGavZM02fos4lc2w6tCchcUBNtJvGqKwhC5JEnx3RYoSX2ucCAwEAAQ==
-----END PUBLIC KEY-----
`)
var privateKey = []byte(`
-----BEGIN RSA PRIVATE KEY-----
MIIBPQIBAAJBALqbHeRLCyOdykC5SDLqI49ArYGYG1mqaH9/GnWjGavZM02fos4l
c2w6tCchcUBNtJvGqKwhC5JEnx3RYoSX2ucCAwEAAQJBAKn6O+tFFDt4MtBsNcDz
GDsYDjQbCubNW+yvKbn4PJ0UZoEebwmvH1ouKaUuacJcsiQkKzTHleu4krYGUGO1
mEECIQD0dUhj71vb1rN1pmTOhQOGB9GN1mygcxaIFOWW8znLRwIhAMNqlfLijUs6
rY+h1pJa/3Fh1HTSOCCCCWA0NRFnMANhAiEAwddKGqxPO6goz26s2rHQlHQYr47K
vgPkZu2jDCo7trsCIQC/PSfRsnSkEqCX18GtKPCjfSH10WSsK5YRWAY3KcyLAQIh
AL70wdUu5jMm2ex5cZGkZLRB50yE6rBiHCd5W1WdTFoe
-----END RSA PRIVATE KEY-----
`)

play.golang.org/p/nsyAw5kYDt

Go Playground always give the same random numbers and therefore the result:

aOleRSXhBT1XR7Al9cxdmM/8KnM2CvQdnNqnvwtq1ivFJ1aITxJUCuTw8ZRB8mY+elhoiUmC4UjM
mwyTKmjqQw==

Javascript encryption


I will encrypt using jsEncrypt :

$(function () {
  $('#but').click(function(){
     var pub = $('#pub').val();
     var crypt = new JSEncrypt();
     crypt.setPublicKey(pub);
     var data = $('#data').val();
     $('#out').val(crypt.encrypt(data));
  });
});

cossackpyra.github.io/april14/html/encrypt.html

And received:

C2uWXwp6OsxLKnr3cXpJIf/RcPzgjlxNXj8IX2R47binEo2dLFhJISDnOioQaM8kAl/lqSSOCLdrYP12Tc/YXQ==

$(function () {
  $('#but').click(function(){
     var key = $('#key').val();
     var crypt = new JSEncrypt();
     crypt.setPrivateKey(key);
     var data = $('#data').val();
     $('#out').val(crypt.decrypt(data));
  });
});

cossackpyra.github.io/april14/html/decrypt.html

Android is not Java


Java is so much and not a damn thing.

Android has android.util.Base64, and Java 8 has java.util.Base64, and there is also org.apache.commons.codec.binary.Base64.

Java does not know how to read certificates in PEM format, what tasks and goals the guide set for the creators of java.security and javax.crypto was a gloom, but obviously did not save disk space.

There is PEMParser in Bouncy Castle . But you can’t pick up Bouncy Castle in an online editor, and in Android it is not clear which Bouncy Castle is used. Therefore, there is Spongy Castle , but it will already be another column in the implementation of cryptography in the form of "Supplement No. 5 to Part 742 — Encryption Registration ”, you cannot answer“ no, no ”to paragraphs 6 and 7.

SNAP-R
(6) Do the products incorporate encryption components produced or furnished by non-US sources or vendors? (If unsure, please explain.)

No.

(7) With respect to your company encryption products, are any of them manufactured outside the United States? If yes, provide manufacturing locations. (Insert “not applicable”, if you are not the principal producer of encryption products.)

No.


Therefore, from private.pem you can remove the module, private and public exhibitors. (This is valid; I originally created the keys using openssl.)

openssl rsa -in private.pem -text -noout
Private-Key: (512 bit)
modulus:
    00:ba:9b:1d:e4:4b:0b:23:9d:ca:40:b9:48:32:ea:
    23:8f:40:ad:81:98:1b:59:aa:68:7f:7f:1a:75:a3:
    19:ab:d9:33:4d:9f:a2:ce:25:73:6c:3a:b4:27:21:
    71:40:4d:b4:9b:c6:a8:ac:21:0b:92:44:9f:1d:d1:
    62:84:97:da:e7
publicExponent: 65537 (0x10001)
privateExponent:
    00:a9:fa:3b:eb:45:14:3b:78:32:d0:6c:35:c0:f3:
    18:3b:18:0e:34:1b:0a:e6:cd:5b:ec:af:29:b9:f8:
    3c:9d:14:66:81:1e:6f:09:af:1f:5a:2e:29:a5:2e:
    69:c2:5c:b2:24:24:2b:34:c7:95:eb:b8:92:b6:06:
    50:63:b5:98:41
prime1:
    00:f4:75:48:63:ef:5b:db:d6:b3:75:a6:64:ce:85:
    03:86:07:d1:8d:d6:6c:a0:73:16:88:14:e5:96:f3:
    39:cb:47
prime2:
    00:c3:6a:95:f2:e2:8d:4b:3a:ad:8f:a1:d6:92:5a:
    ff:71:61:d4:74:d2:38:20:82:09:60:34:35:11:67:
    30:03:61
exponent1:
    00:c1:d7:4a:1a:ac:4f:3b:a8:28:cf:6e:ac:da:b1:
    d0:94:74:18:af:8e:ca:be:03:e4:66:ed:a3:0c:2a:
    3b:b6:bb
exponent2:
    00:bf:3d:27:d1:b2:74:a4:12:a0:97:d7:c1:ad:28:
    f0:a3:7d:21:f5:d1:64:ac:2b:96:11:58:06:37:29:
    cc:8b:01
coefficient:
    00:be:f4:c1:d5:2e:e6:33:26:d9:ec:79:71:91:a4:
    64:b4:41:e7:4c:84:ea:b0:62:1c:27:79:5b:55:9d:
    4c:5a:1e

In Java, it will look like this:

// Private key
		BigInteger modulus = new BigInteger(
				"BA9B1DE44B0B239DCA40B94832EA238F40AD81981B59AA687F7F1A75A319ABD9334D9FA2CE25736C3AB4272171404DB49BC6A8AC210B92449F1DD1628497DAE7",
				16);
		BigInteger exp = new BigInteger(
				"00a9fa3beb45143b7832d06c35c0f3183b180e341b0ae6cd5becaf29b9f83c9d1466811e6f09af1f5a2e29a52e69c25cb224242b34c795ebb892b6065063b59841",
				16);
//Public Key
		BigInteger modulus = new BigInteger(
				"BA9B1DE44B0B239DCA40B94832EA238F40AD81981B59AA687F7F1A75A319ABD9334D9FA2CE25736C3AB4272171404DB49BC6A8AC210B92449F1DD1628497DAE7",
				16);
		BigInteger pubExp = new BigInteger("010001", 16);

The whole Java 8 program:

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import java.util.Base64;
//import javax.xml.bind.DatatypeConverter;
public class HelloWorld {
	public static void main(String[] args) throws Exception {
		try {
			byte[] b1 = decrypt("JutBa0GLHzGrlygxwWr66cizw4W4za+DbzZweNM0iloCD7xEP9LclL013lcksJL5XhjW44\nU+oxpqcX1ZSLhWuA==");
			String s1 = new String(b1, "UTF-8");
			System.out.println(s1);
			byte[] b2 = encrypt("Java kills".getBytes("UTF-8"));
			String s2 = Base64.getEncoder().encodeToString(b2);
			System.out.println(s2);
			byte[] b3 = decrypt(s2);
			String s3 = new String(b3, "UTF-8");
			System.out.println(s3);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static byte[] decrypt(String key) throws Exception {
		BigInteger modulus = new BigInteger(
				"BA9B1DE44B0B239DCA40B94832EA238F40AD81981B59AA687F7F1A75A319ABD9334D9FA2CE25736C3AB4272171404DB49BC6A8AC210B92449F1DD1628497DAE7",
				16);
		BigInteger exp = new BigInteger(
				"00a9fa3beb45143b7832d06c35c0f3183b180e341b0ae6cd5becaf29b9f83c9d1466811e6f09af1f5a2e29a52e69c25cb224242b34c795ebb892b6065063b59841",
				16);
		RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(modulus, exp);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		PrivateKey privKey = kf.generatePrivate(keySpec);
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.DECRYPT_MODE, privKey);
		byte[] decodedStr = Base64.getDecoder().decode(
				key.replace("\n", "").replace("\r", "").replace(" ", ""));
		byte[] plainText = cipher.doFinal(decodedStr);
		return plainText;
	}
	private static byte[] encrypt(byte[] b1) throws Exception {
		BigInteger modulus = new BigInteger(
				"BA9B1DE44B0B239DCA40B94832EA238F40AD81981B59AA687F7F1A75A319ABD9334D9FA2CE25736C3AB4272171404DB49BC6A8AC210B92449F1DD1628497DAE7",
				16);
		BigInteger pubExp = new BigInteger("010001", 16);
		RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, pubExp);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		PublicKey publicKey = kf.generatePublic(keySpec);
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		// byte[] decodedStr = Base64.decode(key, Base64.DEFAULT);
		byte[] plainText = cipher.doFinal(b1);
		return plainText;
	}
}


goo.gl/t27IWw
(Press Compile, Execute)
ik1Dvev7AffP+mOgxkbnYmpZrN9nGCKEzwCA4qsADcSKZFDYC/32B4uzUNSH8D+yCjBbrE5HUDL6vs6W5idG6Q==


Android program
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import android.util.Base64;
import android.util.Log;
public class TestX {
	public static byte[] decrypt(String key) throws Exception {
		BigInteger modulus = new BigInteger(
				"BA9B1DE44B0B239DCA40B94832EA238F40AD81981B59AA687F7F1A75A319ABD9334D9FA2CE25736C3AB4272171404DB49BC6A8AC210B92449F1DD1628497DAE7",
				16);
		BigInteger exp = new BigInteger(
				"00a9fa3beb45143b7832d06c35c0f3183b180e341b0ae6cd5becaf29b9f83c9d1466811e6f09af1f5a2e29a52e69c25cb224242b34c795ebb892b6065063b59841",
				16);
		RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(modulus, exp);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		PrivateKey privKey = kf.generatePrivate(keySpec);
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.DECRYPT_MODE, privKey);
		byte[] decodedStr = Base64.decode(key, Base64.DEFAULT);
		byte[] plainText = cipher.doFinal(decodedStr);
		return plainText;
	}
	public static void test() {
		try {
			byte[] b1 = decrypt("JutBa0GLHzGrlygxwWr66cizw4W4za+DbzZweNM0iloCD7xEP9LclL013lcksJL5XhjW44U+oxpq\ncX1ZSLhWuA==");
			String s1 = new String(b1, "UTF-8");
			Log.i("TEST", s1);
			byte[] b2 = encrypt("Java kills".getBytes("UTF-8"));
			String s2 = Base64.encodeToString(b2, Base64.CRLF);
			Log.i("TEST", s2);
			byte[] b3 = decrypt(s2);
			String s3 = new String(b3, "UTF-8");
			Log.i("TEST", s3);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	private static byte[] encrypt(byte[] b1) throws Exception {
		BigInteger modulus = new BigInteger(
				"BA9B1DE44B0B239DCA40B94832EA238F40AD81981B59AA687F7F1A75A319ABD9334D9FA2CE25736C3AB4272171404DB49BC6A8AC210B92449F1DD1628497DAE7",
				16);
		BigInteger pubExp = new BigInteger("010001", 16);
		RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, pubExp);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		PublicKey publicKey = kf.generatePublic(keySpec);
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		byte[] plainText = cipher.doFinal(b1);
		return plainText;
	}
}


KO: you can encrypt not text, but an AES key.

All spring and good luck.

Only registered users can participate in the survey. Please come in.

How?

  • 70.9% I'm tired of reading it 115
  • 29% I'm tired of writing this 47

Also popular now: