user2858470
user2858470

Reputation: 133

RSA public and private key as String variables in Java

For obvious security reasons i need to encrypt and decrypt User's PIN codes with RSA private and public key, I have found working solution, which looks like:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(512);
    KeyPair rsaKeyPair = kpg.genKeyPair();
    byte[] txt = "This is a secret message.".getBytes();
    System.out.println("Original clear message: " + new String(txt));

    // encrypt
    Cipher cipher;
    try {
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
        txt = cipher.doFinal(txt);
    } catch (Throwable e) {
        e.printStackTrace();
        return;
    }
    System.out.println("Encrypted message: " + new String(txt));

    // decrypt
    try {
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
        txt = cipher.doFinal(txt);
    } catch (Throwable e) {
        e.printStackTrace();
        return;
    }
    System.out.println("Decrypted message: " + new String(txt));

}

everything works fine, but in this example key-pair is not static and generate new values everytime, but I need to use same keys, which are represented as String variables:

public static final String PrivateKey = "MIICXAIBAAKBgQDx0PSJr6zEP9914k1eM+sS8/eW+FenhBQI/jf6ARe8kZHFig9Y"
            + bla bla bla
            + "wdK3jBzObK319yNFr/2LukNZ9Bgv7fS78roBvxbe2gI=";

    public static final String PublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDx0PSJr6zEP9914k1eM+sS8/eW"
            + bla bla bla
            + "jYo5w2Nhxe2cukCQMQIDAQAB";

Is there any way to cast these variables to PublicKey and PrivateKey Class?

Upvotes: 5

Views: 30235

Answers (3)

Jaumzera
Jaumzera

Reputation: 2359

I got this running doing the following:

 public Key loadPrivateKey(String stored) throws GeneralSecurityException {
    PKCS8EncodedKeySpec keySpec =
        new PKCS8EncodedKeySpec(
            Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8)));
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePrivate(keySpec);
  }

  public Key loadPublicKey(String stored) throws GeneralSecurityException {
    byte[] data = Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8));
    X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    return fact.generatePublic(spec);
  }

You must also to remove -----BEGIN PRIVATE KEY-----, -----END PRIVATE KEY----- and all \n from the strings that contain you keys.

Upvotes: 1

Lebowski
Lebowski

Reputation: 608

If I understand what you want, to obtain PublicKey and PrivateKey instances from your static variables you can do, for example, this way:

private static final String privateKeyString = "...";
private static PrivateKey privateKey;
private static final String publicKeyString = "...";
private static PublicKey publicKey;
static {
    KeyFactory kf;
    try {
        kf = KeyFactory.getInstance("RSA");
        byte[] encodedPv = Base64.decodeBase64(privateKeyString);
        PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
        privateKey = kf.generatePrivate(keySpecPv);

        byte[] encodedPb = Base64.decodeBase64(publicKeyString);
        X509EncodedKeySpec keySpecPb = new X509EncodedKeySpec(encodedPb);
        publicKey = kf.generatePublic(keySpecPb);

    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {

    }
}

Upvotes: 3

dave_thompson_085
dave_thompson_085

Reputation: 38821

After (mostly) concurring with @JB that passwords (usually) shouldn't be encrypted, they should be "hashed" -- using a method specifically designed to "stretch" and salt such as scrypt, not a fast hash like SHA-1 -- and also noting that RSA-512 as used in your original code is broken and even RSA-1024 as apparently used in your modification is considered weak:

Your PrivateKey value appears (from its beginning) to be base64 of a plain PKCS#1 DER encoding, which basically is used only by OpenSSL and things that use OpenSSL (format) like older versions of OpenSSH. The Java standard "Sun" providers do not handle this, although I think BouncyCastle might if you want to explore that. For Sun you need to convert it to binary DER from base64; wrap it into PKCS#8 format (which in binary is just adding a header and maybe EOC trailers because the algorithm-specific part of PKCS#8 for RSA is PKCS#1); and put it in a PKCS8EncodedKeySpec and run it through generatePrivate of a KeyFactory of type RSA. See

Alternatively add the header/trailer to make it proper PEM, use OpenSSL to convert it to PKCS#8 (unencrypted), and optionally binary at the same time, and run that through generatePrivate.

Your PublicKey similarly appears to be base64 of an X.509 SubjectPublicKeyInfo encoding, which OpenSSL (but not OpenSSH) uses and standard Java does support under the name "X.509". So just convert from base64 to binary, put in an X509EncodedKeySpec, and run through generatePublic of the RSA KeyFactory. Note if your encryption will be done remote or distributed, which is the usual scenario for publickey-encryption, the encryptor must be certain to use the correct publickey; if an attacker can substitute a wrong publickey they can decrypt and steal at least some of your supposedly secure data. That's why real PK systems don't use a plain publickey, they use a certificate, either X.509 like SSL/TLS and S/MIME, or web-of-trust like PGP.

Upvotes: 0

Related Questions