1

Here's my code:

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.PEMKeyPair;
import java.security.KeyPair;
import java.io.StringReader;
import javax.crypto.Cipher;
import java.util.Base64;
import java.security.interfaces.RSAPrivateKey;

public class Test
{
    public static void main(String[] args) throws Exception
    {
        String key = "-----BEGIN PRIVATE KEY-----\n" +
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqPfgaTEWEP3S9w0t\n" +
"gsicURfo+nLW09/0KfOPinhYZ4ouzU+3xC4pSlEp8Ut9FgL0AgqNslNaK34Kq+NZ\n" +
"jO9DAQIDAQABAkAgkuLEHLaqkWhLgNKagSajeobLS3rPT0Agm0f7k55FXVt743hw\n" +
"Ngkp98bMNrzy9AQ1mJGbQZGrpr4c8ZAx3aRNAiEAoxK/MgGeeLui385KJ7ZOYktj\n" +
"hLBNAB69fKwTZFsUNh0CIQEJQRpFCcydunv2bENcN/oBTRw39E8GNv2pIcNxZkcb\n" +
"NQIgbYSzn3Py6AasNj6nEtCfB+i1p3F35TK/87DlPSrmAgkCIQDJLhFoj1gbwRbH\n" +
"/bDRPrtlRUDDx44wHoEhSDRdy77eiQIgE6z/k6I+ChN1LLttwX0galITxmAYrOBh\n" +
"BVl433tgTTQ=\n" +
"-----END PRIVATE KEY-----";

        String ciphertext = "L812/9Y8TSpwErlLR6Bz4J3uR/T5YaqtTtB5jxtD1qazGPI5t15V9drWi58colGOZFeCnGKpCrtQWKk4HWRocQ==";

        // load the private key
        ASN1Sequence ASN1 = ASN1Sequence.getInstance(key.getBytes());
        PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(ASN1);
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        RSAPrivateKey privateKey = (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);

        // load the ciphertext
        byte[] cipherBytes = Base64.getDecoder().decode(ciphertext);

        // perform the actual decryption
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] plaintextBytes = cipher.doFinal(cipherBytes);
        String plaintext = new String(plaintextBytes);

        System.out.println(plaintext);
    }
}

Running it gives me the following error:

Exception in thread "main" java.lang.IllegalArgumentException: failed to construct sequence from byte[]: unknown tag 13 encountered
        at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
        at MyTest.main(Test.java:48)

I don't understand. OpenSSL's asn1parse has no issue with the key:

$ openssl asn1parse -in test.pem
    0:d=0  hl=4 l= 340 cons: SEQUENCE
    4:d=1  hl=2 l=   1 prim: INTEGER           :00
    7:d=1  hl=2 l=  13 cons: SEQUENCE
    9:d=2  hl=2 l=   9 prim: OBJECT            :rsaEncryption
   20:d=2  hl=2 l=   0 prim: NULL
   22:d=1  hl=4 l= 318 prim: OCTET STRING      [HEX DUMP]:3082013A020100024100A8F7E069311610FDD2F70D2D82C89C5117E8FA72D6D3DFF429F38F8A7858678A2ECD4FB7C42E294A5129F14B7D1602F4020A8DB2535A2B7E0AABE3598CEF4301020301000102402092E2C41CB6AA91684B80D29A8126A37A86CB4B7ACF4F40209B47FB939E455D5B7BE37870360929F7C6CC36BCF2F4043598919B4191ABA6BE1CF19031DDA44D022100A312BF32019E78BBA2DFCE4A27B64E624B6384B04D001EBD7CAC13645B14361D02210109411A4509CC9DBA7BF66C435C37FA014D1C37F44F0636FDA921C37166471B3502206D84B39F73F2E806AC363EA712D09F07E8B5A77177E532BFF3B0E53D2AE60209022100C92E11688F581BC116C7FDB0D13EBB654540C3C78E301E812148345DCBBEDE89022013ACFF93A23E0A13752CBB6DC17D206A5213C66018ACE061055978DF7B604D34

So idk what the issue is.

Any ideas?

neubert
  • 15,947
  • 24
  • 120
  • 212
  • 2
    `ASN1Sequence.getInstance()` expects DER encoded data instead of PEM encoded data. With a DER encoded keys it works. – Topaco Sep 05 '20 at 16:13
  • And `openssl asn1parse` _defaults_ to 'PEM' input (more exactly, base64 input with the dash-BEGIN/END lines ignored, whereas nearly all other openssl functions do use the dash-BEGIN/END lines). Although you can specify `openssl asn1parse -inform der` and get a similar/related error. – dave_thompson_085 Sep 05 '20 at 17:03

1 Answers1

3

The problem is that you are getting bytes of Base64 String and not raw ASN1 data.

Remove the -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- lines. Also remove all \n. Then decode the PEM key using Base64 decoder :

String key =
        "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqPfgaTEWEP3S9w0t" +
        "gsicURfo+nLW09/0KfOPinhYZ4ouzU+3xC4pSlEp8Ut9FgL0AgqNslNaK34Kq+NZ" +
        "jO9DAQIDAQABAkAgkuLEHLaqkWhLgNKagSajeobLS3rPT0Agm0f7k55FXVt743hw" +
        "Ngkp98bMNrzy9AQ1mJGbQZGrpr4c8ZAx3aRNAiEAoxK/MgGeeLui385KJ7ZOYktj" +
        "hLBNAB69fKwTZFsUNh0CIQEJQRpFCcydunv2bENcN/oBTRw39E8GNv2pIcNxZkcb" +
        "NQIgbYSzn3Py6AasNj6nEtCfB+i1p3F35TK/87DlPSrmAgkCIQDJLhFoj1gbwRbH" +
        "/bDRPrtlRUDDx44wHoEhSDRdy77eiQIgE6z/k6I+ChN1LLttwX0galITxmAYrOBh" +
        "BVl433tgTTQ=";

byte[] keyBytes = Base64.getDecoder().decode(key);
// load the private key
ASN1Sequence ASN1 = ASN1Sequence.getInstance(keyBytes);
PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(ASN1);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
RSAPrivateKey privateKey = (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);

also you could ommit the call to ASN1Sequence.getInstance after decoding the Base64 :

byte[] keyBytes = Base64.getDecoder().decode(key);
PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(keyBytes);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
RSAPrivateKey privateKey = (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
Michał Krzywański
  • 15,659
  • 4
  • 36
  • 63
  • I want people to be able to upload keys to a website without requiring them to manually edit them prior to upload. But I guess I can use regex to remove those lines... – neubert Sep 05 '20 at 16:52
  • Yes, you can just call `replace` on the key `String` or use a regex. – Michał Krzywański Sep 05 '20 at 16:53
  • Also you can consider using `PEMParser` if you want to read pem private keys. Have a look [here](https://stackoverflow.com/questions/14919048/bouncy-castle-pemreader-pemparser) – Michał Krzywański Sep 05 '20 at 16:54
  • 1
    For this format _only_ (PKCS8 unencrypted) you can also skip Bouncy and use a standard JCE `KeyFactory.getInstance("RSA")` – dave_thompson_085 Sep 05 '20 at 17:05