juniorbansal
juniorbansal

Reputation: 1271

Java - How to fix InvalidKeyException: Wrong algorithm type - RSA

I got a Private JKS file and a Password file for it. I saw the internet, wrote a java program which will print out the private key as a string.

Similarly I got a public key file with .cer extension. Similarly I printed out the contents of this file too as a string

My idea is to write a java program which has 2 methods encrypt and decrypt.I wrote all the stuff. But when I try to use my encrypt and decrypt function i am seeing InvalidKeyException: Wrong algorithm type. I printed out the algorithm from the jks file and I see it as RSA. In the public certificate the algorithm is printed as SHA1withRSA

here are my programs

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import org.apache.commons.codec.binary.Base64;

public class ExtractKeys {

    private static String privateKeyalgorithm = "";
    private static String publicKeyAlgorithm = "";

    public static void main(String args[]) throws Exception {

        String publicCertificate = "C:\\QA\\keys\\sis.cer";

        String privateKeyStore = "C:\\QA\\keys\\sis.jks";
        String privateKeyStorePassword = "sis";
        String alias = "sis";
        String aliasPassword = "sis";

        ExtractKeys myep = new ExtractKeys();

        myep.printPrivateKey(privateKeyStore, privateKeyStorePassword, alias, aliasPassword);
        myep.printPublicKey(publicCertificate);

    }

    public String printPublicKey(String source) throws Exception {
        X509Certificate cert = null;
        InputStream fis = null;
        ByteArrayInputStream bais = null;
        Base64 encoder = new Base64(64);

        fis = new FileInputStream(source);
        byte value[] = new byte[fis.available()];
        fis.read(value);
        bais = new ByteArrayInputStream(value);
        java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509");
        cert = (X509Certificate) cf.generateCertificate(bais);

        String cert_begin = "-----BEGIN CERTIFICATE-----\n";
        String end_cert = "-----END CERTIFICATE-----";
        publicKeyAlgorithm = cert.getSigAlgName();
        byte[] derCert = cert.getEncoded();
        String pemCertPre = new String(encoder.encode(derCert));
        String pemCert = cert_begin + pemCertPre + end_cert;
        System.out.println(pemCert);
        System.out.println(publicKeyAlgorithm);
        return pemCert;

    }

    public String printPrivateKey(String fileName, String keystorepass, String aliasName, String aliaspass) throws Exception {

        KeyStore ks = KeyStore.getInstance("JKS");
        Base64 encoder = new Base64(64);

        char[] keyStorePassPhrase = keystorepass.toCharArray();

        File certificateFile = new File(fileName);

        ks.load(new FileInputStream(certificateFile), keyStorePassPhrase);

        char[] aliasPassPhrase = aliaspass.toCharArray();

        KeyPair kp = getPrivateKey(ks, aliasName, aliasPassPhrase);

        PrivateKey privKey = kp.getPrivate();

        privateKeyalgorithm = privKey.getAlgorithm();

        String b64 = encoder.encodeAsString(privKey.getEncoded());

        System.out.println("-----BEGIN PRIVATE KEY-----");

        System.out.println(b64);

        System.out.println("-----END PRIVATE KEY-----");

        System.out.println(privateKeyalgorithm);

        return b64;

    }

    private KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) {

        try {

            // Get private key

            Key key = keystore.getKey(alias, password);

            if (key instanceof PrivateKey) {

                // Get certificate of public key

                Certificate cert = keystore.getCertificate(alias);

                // Get public key

                PublicKey publicKey = cert.getPublicKey();

                // Return a key pair

                return new KeyPair(publicKey, (PrivateKey) key);

            }

        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();

        } catch (KeyStoreException e) {
            e.printStackTrace();

        }

        return null;

    }

    public String returnPrivateKeyAlgo() {
        return privateKeyalgorithm;
    }

    public String returnPublicKeyAlgo() {
        return publicKeyAlgorithm;
    }

The outputs are strings of public key, private key, and their alogorithms

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZehr60/61+4ao
8Bigkamzt3RJZcY9ggE4YuQ1ALY8pDXARDQ3OqPohySw4y+Ebrk4y/Rwzm21mhaU
jC33BNSNUetRAgBDU+/nT3/gRZdIeJRfFdiDtl/Ms2LYxo41nYTeGJEqoW3fivI2
cUG/tDKSPjaoGwnz/kVNIsZXJEcZCAfdIg1UH0wBeQ5qLR4rsseE0I7vVAfvDsMl
-----END PRIVATE KEY-----
RSA
-----BEGIN CERTIFICATE-----
MIIGiTCCBXGgAwIBAgIKYMbAHgAAAAUgKzANBgkqhkiG9w0BAQUFADB5MRMwEQYK
CZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGc2Nod2FiMRYwFAYKCZIm
-----END CERTIFICATE-----
SHA1withRSA

Now I got the public and private keys as string values. along with their algorithms.

Next I wrote another sample program.I got 2 methods in it. encrypt and decrypt which also accepts these keys.

So ideally i would be passing the public key to encrypt method and the private key to decrypt method

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import **.CryptoService;
import **.CryptoServiceException;


public class CryptoServiceImpl implements CryptoService {

public byte[] encrypt(byte[] data, String key) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            Base64 decoder = new Base64(64);

            // decode the base64 encoded string
            byte[] decodedKey = decoder.decode(key);
            // rebuild key using SecretKeySpec
            final SecretKeySpec originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "RSA");

            cipher.init(Cipher.ENCRYPT_MODE, originalKey);
            final String encryptedString = Base64.encodeBase64String(cipher.doFinal(data));
            return encryptedString.getBytes();
        } catch (Exception e) {
            throw new CryptoServiceException("Cannot encrypt data using key '", e);
        }

    }

    public byte[] decrypt(byte[] data, String key) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            Base64 decoder = new Base64(64);

            // decode the base64 encoded string
            byte[] decodedKey = decoder.decode(key);
            // rebuild key using SecretKeySpec
            final SecretKeySpec originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "RSA");

            cipher.init(Cipher.DECRYPT_MODE, originalKey);
            final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(new String(data))));
            return decryptedString.getBytes();
        } catch (Exception e) {
            throw new CryptoServiceException("Cannot decrypt data using key '", e);

        }
    }
}

Now I got a Junit which when executed throws this exception

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import junit.framework.Assert;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import **.CryptoServiceImpl;

public class TestCryptoService {

    String privateKeyStore = "C:\\QA\\keys\\sis.jks";
    String privateKeyStorePassword = "sistest";
    String alias = "sis";
    String aliasPassword = "sistest";
    static ExtractKeys myep = new ExtractKeys();
    CryptoService service = new CryptoServiceImpl();
    String publicCertificate = "C:\\QA\\keys\\sis.cer";;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {
    }

@Test
    public void testCryptoServiceForAESWithAsymmetricKeys() throws Exception {

        String publicKey = myep.printPublicKey(publicCertificate);

        byte[] encryptedValue = service.encrypt(new String("abcd").getBytes(), publicKey);
        System.out.println(new String(encryptedValue));

        String privateKey = myep.printPrivateKey(privateKeyStore, privateKeyStorePassword, alias, aliasPassword);
        byte[] decryptedValue = service.decrypt(encryptedValue, privateKey);

        System.out.println(new String(decryptedValue));
        Assert.assertEquals("abcd", new String(decryptedValue));
    }
}

and Here's the exception

*.CryptoServiceException: Cannot encrypt data using key '
    at *.impl.CryptoServiceImpl.encrypt(CryptoServiceImpl.java:59)
    at *.TestCryptoService.testCryptoServiceForAESWithAsymmetricKeys(TestCryptoService.java:101)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56)
    at java.lang.reflect.Method.invoke(Method.java:620)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    	at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.security.InvalidKeyException: Wrong algorithm type
    at com.ibm.crypto.provider.RSAKeyFactory.engineTranslateKey(Unknown Source)
    at com.ibm.crypto.provider.RSAKeyFactory.toRSAKey(Unknown Source)
    at com.ibm.crypto.provider.RSACipher.engineGetKeySize(Unknown Source)
    at javax.crypto.Cipher.b(Unknown Source)
    at javax.crypto.Cipher.a(Unknown Source)
    at javax.crypto.Cipher.a(Unknown Source)
    at javax.crypto.Cipher.a(Unknown Source)
    at javax.crypto.Cipher.init(Unknown Source)
    at javax.crypto.Cipher.init(Unknown Source)
    at *.impl.CryptoServiceImpl.encrypt(CryptoServiceImpl.java:55)
    ... 28 more

Upvotes: 1

Views: 4614

Answers (1)

dave_thompson_085
dave_thompson_085

Reputation: 38821

A certificate is not a public key although it contains one; more importantly, either a private key or public key or certificate for a public-key-crypto (aka asymmetric) scheme like RSA is not a symmetric key. Java SecretKeySpec is only for symmetric keys used with symmetric algorithms, like AES.

You need to represent your RSA keys using subtypes of java.security.PrivateKey and java.security.PublicKey. The way you do this is with a KeyFactory for RSA, or a CertificateFactory (as you already did).

For a PKCS#8-encoded private key, which is what you encoded and wrote out, decode it from PEM/base64 to byte[], then:

PKCS8EncodedKeySpec spec = new PKCSEncodedKeySpec (bytearray);
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privkey = factory.generatePrivate (spec);
// use privkey in a Cipher.getInstance("RSA") to decrypt or sign

For a certificate do basically what you already did:

InputStream is = /* something that returns contents of cert file */;
// you *can* decode from PEM/base64 first, but you don't need to;
// CertificateFactory-X.509 handles both
CertificateFactory cf = CertificateFactory.getInstance ("X.509");
Certificate cert = cf.generateCertificate (is);
// use cert.getPublicKey() for RSA encrypt or verify

Aside: sigAlgName for a certificate does NOT tell about the key in the cert. It is entirely possible for a cert signed SHA1withRSA to contain a DSA or ECC key that cannot be used for RSA, and conversely possible for a cert that contains a perfectly good RSA key to be signed with a different RSA variant (like SHA256withRSA) or an entirely different algorithm (like sha1-DSA or sha2-ECDSA).

Also note: you are explicitly importing java.security.cert.Certificate; that's good. JRE also has an older and deprecated java.security.Certificate which you should not use, but if you just say Certificate some IDEs (like mine) may get confused and give you the wrong one.

Upvotes: 1

Related Questions