https://docs.oracle.com/en/java/javase/11/security/index.html describes a Java interface but does not specify the default algorithms for things like RSA encryption. I need to interface with code using the Java 11 java.security model for PK encryption using RSA on a linux server.

From what I can tell, Java uses PKCS v2.2 and probably uses sha256 (though they don't say). All of the examples on Oracle's site describe interfacing to java.security from Java only; most of what I can find on the net (or here) is quite old or doesn't actually describe the non-Java interface.

Q: Does anyone know of any working examples for using another language (preferably Perl or C) to interface with the Java-11 java.security library for PK encryption using RSA from linux?

If I can find any reference to a working library that will interface properly with Java-11's java.security I'll at least have working specs to start from -- or just interface directly to it with Inline.

Perl's Crypt::PK::RSA uses TomCrypt (https://github.com/libtom/libtomcrypt). This is a nice C implementation but it uses a v1.5 key padding, which isn't going to work with Java using v2.2. If I can find a working C v2.2 implementation I can try and graft it into TomCrypt; that or just a working stand-along RSA encryption algorithm in C that uses v2.2.

At this point I've tried using Crypt::PK::RSA with all of the available options for v1.5 & oeap using sha1 & sha256 to reproduce encrypting a given string to known ciphertext using both high- and low-endian transform to hex (via unpack H* and h*) without being able to reproduce the ciphertext hex output from Java.

At this point I've also tried using key pairs generated by OpenSSL & TomCrypt (via Crypt::PK::RSA) and can't even get Java to read them so I can't even generate a throwaway key to use here as an example.

1

There are 1 answers

0
dave_thompson_085 On

As I commented, for any decent modern encryption scheme, you can't validate a ciphertext by comparing it to another ciphertext, because by design they are always different. This is required to satisfy the now generally-agreed criterion for security, that an adversary cannot gain any knowledge about the plaintext, other than its maximum size, by knowledge of any number of other plaintext-ciphertext pairs other than the one(s) being attacked. See https://en.wikipedia.org/wiki/Ciphertext_indistinguishability .

Instead, to determine whether a ciphertext is correct, you can (only) see if it decrypts (at all, and to the desired value). To answer your previous question, since it is now closed, here is an example of using perl Crypt::PK::RSA to encrypt a value that can be decrypted by Java as you specified:

$ keytool -genkeypair -keyalg rsa -keysize 2048 -dname CN=test -keystore SO76148741.ks -storepass sekrit
Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 90 days
        for: CN=test
$ keytool -exportcert -rfc -keystore SO76148741.ks -storepass sekrit |openssl x509 -noout -pubkey |tee SO76148741.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Q7u21KbgmKk+FlhRHs1
5L9odmTFvAy+UpVFzfJpB7YZ0zYcL2zMPFng1GuKUOzakUc8oRhGIoyjAYwKgyP2
UhEd4fIKzspXv3BtShy9yiTRnEiy4ZkzB9hHEiQ6To2j5LsO56Lv5sJVtW/sRhlJ
/eiGjW5W0xRLFHC/gJpaJWi5lEcKkWIYpR0DSmz/2oCA8CGfKPAg6JN8TSaEp2HK
XANZmbOmn4D0s3u3b0eWnKU+1FnrMbUpiavJMeXqcQGKc7wbNWUAUJ8jbFihgj+/
ns+nRZVWmwpvoJil4NUlQmaWr1tId57SD5/vZbAwHiHxUGeYnGO1SidGjxucSP/C
awIDAQAB
-----END PUBLIC KEY-----
$ cat SO76148741.pl
use Crypt::PK::RSA;
my $pub = Crypt::PK::RSA->new('SO76148741.pub');
my $enc = $pub->encrypt('ImportantSecret','v1.5');
print unpack('H*',$enc).$/;
$ perl SO76148741.pl | tee SO76148741.crypt
749b3dc50a84814bc83ddb357365ee22cf681c3da5f2a1dbb074a0e4a65d16b8be8a89c74416f5664a6c382b4f8d53952da2cd7d8d7e45dde654362f89d6dc7602eddb76fe43e50bf57ff79e52d30f405bf0038034fcfba861e58d31d883d87ef30d495a37184168060d41cd919cb95e2978abcb4e0ea3955381ca8a989989eeb5fcf6885952f00cd0b335cab5b29d7baddef8a1ad762355b5de7c8f9a8880230b0c06491f9f180ba2aa17df8e91c7eb0f90090e9000a2563ac279188629ebc2fda50e7c8815f4f6b2a27fafa66206d46f906b93a50331cd02c026e4cc4e8cac759ff6d3bdd36707f7416b6f8bea7da8ed7d7a636b6588da4e6d15112edd8922
$ cat SO76148741.java
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.security.*;
import javax.crypto.*;

public class SO76148741 {
    public static void main (String[] args) throws Exception {
        KeyStore ks = KeyStore.getInstance(new File("SO76148741.ks"), "sekrit".toCharArray());
        PrivateKey priv = (PrivateKey) ks.getKey("mykey","sekrit".toCharArray());
        String hex = new String(Files.readAllBytes(Paths.get("SO76148741.crypt")),"ASCII");
        byte[] crypt = new byte[hex.length()/2];
        for( int i = 0; i < crypt.length; i++ )
            crypt[i] = (byte) Integer.parseInt(hex.substring(i*2,i*2+2),16);

        Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        ciph.init(Cipher.DECRYPT_MODE, priv);
        byte[] plain = ciph.doFinal(crypt);
        System.out.println (new String(plain,"ASCII"));
    }
}
$ java SO76148741.java
ImportantSecret