13

I have a certificate in a text file, its contents look like:

-----BEGIN PUBLIC KEY-----
xxxx
xxxx
xxxx
-----END PUBLIC KEY-----

I believe this is a pem encoded certificate? So I want to load it now, I'm trying the following:

X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
    readFileToByteArray("keyfile"));

but I get an InvalidKeySpecException.

If I load the file, cut off the begin/end header/footer, then base64 decode the 'xxxx' contents, I don't get any complaints:

String contents = readFileToString("keyfile");
contents = contents.replace("-----BEGIN PUBLIC KEY-----", "");
contents = contents.replace("-----END PUBLIC KEY-----", "");
byte[] prepared = Base64.decode(contents);

// ok.
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(prepared);

is this the right way to load the key file? I see there's also a RSAPublicKeySpec class, which, based on the name, seems like something I'd be interested in here. But I believe it is only for generating certificates, not reading existing ones?

Thanks

user3203425
  • 2,919
  • 4
  • 29
  • 48
  • "I have a certificate"... no, you have a public key, not a certificate. However, what you do to get a key instance is correct. – eis Dec 27 '17 at 10:32

3 Answers3

14

There is a good summary of the common key formats here. As indicated by the javadocs for X509EncodedKeySpec this class is designed to convert between the SubjectPublicKeyInfo ASN.1 struct that is in the X.509 standard and Java public key formats. And since the first link indicated that a file of the form

-----BEGIN PUBLIC KEY-----
xxxx
xxxx
-----END PUBLIC KEY-----

is indeed a SubjectPublicKeyInfo, you are correctly parsing the file. There is one final step you're missing, and that's to convert your X509EncodedKeySpec into a public key. That is the function of the KeyFactory class. To extend your example by two more lines, it would be

KeyFactory kf = KeyFactory.getInstance("RSA"); // Assuming this is an RSA key
RSAPublicKey rsaPubKey = (RSAPublicKey) kf.generatePublic(publicKeySpec);
Community
  • 1
  • 1
President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
1

The answer is not straightforward. Following code works for me when loading RSA public key string (encoded in PEM format as mentioned above) to java.security.PublicKey object. Note: the headers and ends should be stripped beforehand.

    public PublicKey convertToPublicKey(String publicKeyString) {
    try {
        byte[] decodedPublicKey = Base64.decode(publicKeyString, Base64.DEFAULT);
        org.spongycastle.asn1.ASN1InputStream in = new org.spongycastle.asn1.ASN1InputStream(decodedPublicKey);
        org.spongycastle.asn1.ASN1Primitive obj = in.readObject();
        org.spongycastle.asn1.pkcs.RSAPublicKey keyStruct = org.spongycastle.asn1.pkcs.RSAPublicKey.getInstance(obj);
        java.security.spec.RSAPublicKeySpec keySpec = new java.security.spec.RSAPublicKeySpec(keyStruct.getModulus(), keyStruct.getPublicExponent());
        java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Then, the java.security.PublicKey instance can be used to encrypt your string as follows:

Cipher rsaCipher = Cipher.getInstance("RSA/None/PKCS1Padding", "SC");
rsaCipher.init(Cipher.ENCRYPT_MODE, apiPublicKey);
byte[] ENCRYPTED_YOUR_STRING = rsaCipher.doFinal(YOUR_STRING);

The bytes can then be converted to string by: Base64.encodeToString(ENCRYPTED_YOUR_STRING, Base64.DEFAULT)

The solution is tested on Android API 25 Platform and spongycastle 1.56

user3786340
  • 190
  • 1
  • 8
-1

Use this class

Note: use RSA only by less then 129 byte string.

public class RSA
{
private PublicKey internalPublicKey;
private PrivateKey internalPrivateKey;
private KeyPairGenerator kpg = null;
private int SIZE = 4096;

public RSA(int size)
{
    try
    {
        SIZE = size;
        kpg = KeyPairGenerator.getInstance("RSA");
        init();
    }
    catch(Exception e){}
}

public RSA()
{
    this(1024);
}

private void init()
{
    kpg.initialize(SIZE, new SecureRandom());

    KeyPair kp = kpg.genKeyPair();
    internalPublicKey = kp.getPublic();
    internalPrivateKey = kp.getPrivate();
}

public int getSize()
{
    return SIZE;
}

public PublicKey getPublic()
{
    return internalPublicKey;
}

public PrivateKey getPrivate()
{
    return internalPrivateKey;
}

public String getPublicModule()
{
    String s = internalPublicKey.toString();
    return s.substring(s.indexOf("modulus")+8, s.indexOf(",publicExponent"));
}

public String getPublicExponent()
{
    String s = internalPublicKey.toString();
    return s.substring(s.indexOf("publicExponent")+15, s.lastIndexOf("}"));
}

public String getPrivateExponent()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("privateExponent")+16, s.indexOf(",primeP"));
}

public String getPrivatePrimP()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeP=")+7, s.indexOf(",primeQ"));
}

public String getPrivatePrimQ()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeQ=")+7, s.indexOf(",primeExponentP"));
}

public String getPrivatePrimExponentP()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeExponentP=")+15, s.indexOf(",primeExponentQ"));
}

public String getPrivatePrimExponentQ()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeExponentQ=")+15, s.indexOf(",crtCoefficient"));
}

public String getPrivateCrtCoefficient()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("crtCoefficient=")+15, s.lastIndexOf(","));
}

public byte[] getPublicKey()
{
    return internalPublicKey.getEncoded();
}

public byte[] getPrivateKey()
{
    return internalPrivateKey.getEncoded();
}

public String getPublicKeyAsString()
{
    return Base64.encodeToString(internalPublicKey.getEncoded(), Base64.DEFAULT);
}

public String getPrivateKeyAsString()
{
    return Base64.encodeToString(internalPrivateKey.getEncoded(), Base64.DEFAULT);
}

public byte[] getEncrypt(String plain)
{
    try
    {
        //Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, internalPublicKey);

        return cipher.doFinal(plain.getBytes("UTF-8"));
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: getEncrypt(String x), ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public String getStringEncrypt(String plain)
{
    return new String(getEncrypt(plain), Charset.forName("UTF-8"));
}

public byte[] getDecrypt(byte[] encryptedBytes)
{
    try
    {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, internalPrivateKey);

        return cipher.doFinal(encryptedBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: getDecrypt(byte[] x), ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public byte[] getDecrypt(String encrypted)
{
    return getDecrypt(encrypted.getBytes());
}

public String getStringDecrypt(byte[] encryptedBytes)
{
    return new String(getDecrypt(encryptedBytes), Charset.forName("UTF-8"));
}

public String getStringDecrypt(String encrypted)
{
    return new String(getDecrypt(encrypted), Charset.forName("UTF-8"));
}

public static byte[] getEncrypt(String plain, String modulus, String exponent)
{
    try
    {
        BigInteger modBigInteger = new BigInteger(modulus, 16);
        BigInteger exBigInteger = new BigInteger(exponent, 16);
        RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);

        KeyFactory factory = KeyFactory.getInstance("RSA");
        PublicKey pk = factory.generatePublic(spec);

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pk);

        return cipher.doFinal(plain.getBytes("UTF-8"));
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: getEncrypt, ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static String getStringEncrypt(final String plain, String modulus, String exponent)
{
    return Base64.encodeToString(getEncrypt(plain, modulus, exponent), Base64.DEFAULT);
}

public static byte[] getDecrypt(byte[] encryptedBytes, String modulus, String publicExpo, String privateExpo, String primP, String primQ, String ePrimP, String ePrimQ, String cof)
{
    try
    {
        BigInteger module = new BigInteger(modulus, 16);
        BigInteger expo1 = new BigInteger(publicExpo, 16);
        BigInteger expo2 = new BigInteger(privateExpo, 16);
        BigInteger prim_P = new BigInteger(primP, 16);
        BigInteger prim_Q = new BigInteger(primQ, 16);
        BigInteger prim_EP = new BigInteger(ePrimP, 16);
        BigInteger prim_EQ = new BigInteger(ePrimQ, 16);
        BigInteger coefficient = new BigInteger(cof, 16);
        /*BigInteger module = new BigInteger(1, Base64.encode(modulus.getBytes(), Base64.DEFAULT));
        BigInteger expo1 = new BigInteger(1, Base64.encode(publicExpo.getBytes(), Base64.DEFAULT));
        BigInteger expo2 = new BigInteger(1, Base64.encode(privateExpo.getBytes(), Base64.DEFAULT));
        BigInteger prim_P = new BigInteger(1, Base64.encode(primP.getBytes(), Base64.DEFAULT));
        BigInteger prim_Q = new BigInteger(1, Base64.encode(primQ.getBytes(), Base64.DEFAULT));
        BigInteger prim_EP = new BigInteger(1, Base64.encode(ePrimP.getBytes(), Base64.DEFAULT));
        BigInteger prim_EQ = new BigInteger(1, Base64.encode(ePrimQ.getBytes(), Base64.DEFAULT));
        BigInteger coefficient = new BigInteger(1, Base64.encode(cof.getBytes(), Base64.DEFAULT));*/

        RSAPrivateCrtKeySpec spec = new RSAPrivateCrtKeySpec(module, expo1, expo2, prim_P, prim_Q, prim_EP, prim_EQ, coefficient);

        KeyFactory factory = KeyFactory.getInstance("RSA");
        PrivateKey pk = factory.generatePrivate(spec);

        Cipher cipher1 = Cipher.getInstance("RSA");
        cipher1.init(Cipher.DECRYPT_MODE, pk);

        //return cipher1.doFinal(Base64.decode(encryptedBytes, Base64.DEFAULT));
        return cipher1.doFinal(encryptedBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static String getStringDecrypt(byte[] encryptedBytes, String modulus, String publicExpo, String privateExpo, String primP, String primQ, String ePrimP, String ePrimQ, String cof)
{
    return Converter.byteToString_UTF8(getDecrypt(encryptedBytes, modulus, publicExpo, privateExpo, primP, primQ, ePrimP, ePrimQ, cof));
}

public static byte[] getDecrypt(final byte[] encryptedBytes, byte[] privateKey)
{
    try
    {
        KeyFactory keyFac = KeyFactory.getInstance("RSA");
        KeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        PrivateKey pk = keyFac.generatePrivate(keySpec);

        Cipher cipher1 = Cipher.getInstance("RSA");
        cipher1.init(Cipher.DECRYPT_MODE, pk);
        return cipher1.doFinal(encryptedBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static String getStringDecrypt(final byte[] encryptedBytes, byte[] privateKey)
{
    return Converter.byteToString_UTF8(getDecrypt(encryptedBytes, privateKey));
}

public static String sign(String plainText, PrivateKey privateKey)
{
    try
    {
        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(privateKey);
        privateSignature.update(plainText.getBytes());

        byte[] signature = privateSignature.sign();

        return Base64.encodeToString(signature, Base64.DEFAULT);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: sign(), ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static boolean verify(String plainText, String signature, PublicKey publicKey)
{
    Signature publicSignature;

    try
    {
        publicSignature = Signature.getInstance("SHA256withRSA");
        publicSignature.initVerify(publicKey);
        publicSignature.update(plainText.getBytes());

        byte[] signatureBytes = Base64.decode(signature, Base64.DEFAULT);

        return publicSignature.verify(signatureBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: verify(), ", Constants.DEFAULT_ALERT_STATE);
    }

    return false;
}

}

Ali Bagheri
  • 3,068
  • 27
  • 28