14

My task is the following:

  • Retrieve my public and private key from the keystore I created.
  • Use these keys to encrypt a paragraph using my RSA 2048-bit public key.
  • Digitally sign the result using the DSA-SHA-1 signature algorithm.
  • Save the digital signature output on a file called output.dat.

The program below is throwing error : "java.security.InvalidKeyException: No installed provider supports this key: sun.security.provider.DSAPublicKeyImpl".

import java.security.*;
import java.security.KeyStore.*;
import java.io.*;
import java.security.PublicKey;
import java.security.PrivateKey;
import javax.crypto.Cipher;
import java.nio.charset.*;
import sun.security.provider.*;
import  javax.crypto.*;

public class Code {

/**
 * @param args the command line arguments
 */
    public static void main(String[] args) {

        try {

            /* getting data for keystore */

            File file = new File(System.getProperty("user.home") + File.separatorChar + ".keystore");
            FileInputStream is = new FileInputStream(file);
            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());

            /*Information for certificate to be generated */ 
            String password = "abcde";
            String alias = "mykeys";
            String alias1 = "skeys";

            String filepath ="C:\\email.txt";

            /* getting the key*/
            keystore.load(is, password.toCharArray());
            PrivateKey key = (PrivateKey)keystore.getKey(alias, "bemylife".toCharArray());
            //PrivateKey key = cert1.getPrivateKey();
            //PublicKey key1= (PrivateKey)key;

            /* Get certificate of public key */
            java.security.cert.Certificate cert = keystore.getCertificate(alias); 

            /* Here it prints the public key*/
            System.out.println("Public Key:");
            System.out.println(cert.getPublicKey());

            /* Here it prints the private key*/
            System.out.println("\nPrivate Key:");
            System.out.println(key);

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

            String cleartextFile = "C:\\email.txt";
            String ciphertextFile = "D:\\ciphertextRSA.png";

            FileInputStream fis = new FileInputStream(cleartextFile);
            FileOutputStream fos = new FileOutputStream(ciphertextFile);
            CipherOutputStream cos = new CipherOutputStream(fos, cipher);

            byte[] block = new byte[32];
            int i;
            while ((i = fis.read(block)) != -1) {
                cos.write(block, 0, i);
            }
            cos.close();


            /* computing the signature*/
            Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");
            dsa.initSign(key);
            FileInputStream f = new FileInputStream(ciphertextFile);
            BufferedInputStream in = new BufferedInputStream(f);
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) >= 0) {
               dsa.update(buffer, 0, len);
           };
           in.close();

           /* Here it prints the signature*/
           System.out.println("Digital Signature :");
           System.out.println( dsa.sign());

           /* Now Exporting Certificate */
           System.out.println("Exporting Certificate. ");
           byte[] buffer_out = cert.getEncoded();
           FileOutputStream os = new FileOutputStream(new File("d:\\signedcetificate.cer"));
           os.write(buffer_out);
           os.close();

           /* writing signature to output.dat file */
           byte[] buffer_out1 = dsa.sign();
           FileOutputStream os1 = new FileOutputStream(new File("d:\\output.dat"));
           os1.write(buffer_out1);
           os1.close();

       } catch (Exception e) {System.out.println(e);}

   }
}
Zack Ef
  • 143
  • 1
  • 1
  • 5
  • 1
    I believe this is more programming related (Implementation details), rather than security related. – aularon Nov 06 '13 at 09:32
  • Can you verify that the alias of your public/private keypair in your keystore is "skeys"? If it isn't it would explain why your public key couldn't be found. You can check with keytool: keytool -list -v -keystore ~/.keystore -alias "skeys" – Gene Gotimer Nov 12 '13 at 15:07
  • The change which I made is java.security.cert.Certificate cert = keystore.getCertificate(alias); instead of (alias1). Because upon your suggestion I tried to check for the alias1 name "skeys" is not there and now it is displaying the public and private key but another error which I mentioned above the program. – Zack Ef Nov 13 '13 at 07:57

4 Answers4

9

You have to read it from the keystore file (which probably ends in .jks) into a java.security.KeyStore object.

/**
 * Reads a Java keystore from a file.
 * 
 * @param keystoreFile
 *          keystore file to read
 * @param password
 *          password for the keystore file
 * @param keyStoreType
 *          type of keystore, e.g., JKS or PKCS12
 * @return the keystore object
 * @throws KeyStoreException
 *           if the type of KeyStore could not be created
 * @throws IOException
 *           if the keystore could not be loaded
 * @throws NoSuchAlgorithmException
 *           if the algorithm used to check the integrity of the keystore
 *           cannot be found
 * @throws CertificateException
 *           if any of the certificates in the keystore could not be loaded
 */
public static KeyStore loadKeyStore(final File keystoreFile,
    final String password, final String keyStoreType)
    throws KeyStoreException, IOException, NoSuchAlgorithmException,
    CertificateException {
  if (null == keystoreFile) {
    throw new IllegalArgumentException("Keystore url may not be null");
  }
  LOG.debug("Initializing key store: {}", keystoreFile.getAbsolutePath());
  final URI keystoreUri = keystoreFile.toURI();
  final URL keystoreUrl = keystoreUri.toURL();
  final KeyStore keystore = KeyStore.getInstance(keyStoreType);
  InputStream is = null;
  try {
    is = keystoreUrl.openStream();
    keystore.load(is, null == password ? null : password.toCharArray());
    LOG.debug("Loaded key store");
  } finally {
    if (null != is) {
      is.close();
    }
  }
  return keystore;
}

Once you have the KeyStore, you can get to the Certificate and the public and private keys.

But using that to sign text and save it in a file is more involved, and easy to do wrong. Take a look at Sign string using given Public Key and replace the getKeyPair method with one that uses the KeyStore. Something along the lines of

public static KeyPair getKeyPair(final KeyStore keystore, 
    final String alias, final String password) {
  final Key key = (PrivateKey) keystore.getKey(alias, password.toCharArray());

  final Certificate cert = keystore.getCertificate(alias);
  final PublicKey publicKey = cert.getPublicKey();

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

(obviously a little rougher, I didn't have a sample handy)

Community
  • 1
  • 1
Gene Gotimer
  • 6,781
  • 2
  • 27
  • 44
3

The problem is that a DSA key is unsuitable for RSA encryption. You need an RSA key for encryption, maybe you can switch your signature algorithm to RSA/SHA1 to avoid the need for two keys..

Drunix
  • 3,224
  • 7
  • 25
  • 47
1
trusted.load(in, ((PBCApplication) context.getApplicationContext()).getBuildSettings().getCertificatePass());
Enumeration enumeration = trusted.aliases();

while (enumeration.hasMoreElements()) {
    String alias = (String) enumeration.nextElement();
    System.out.println("alias name: " + alias);
    Certificate certificate = trusted.getCertificate(alias);
    certificate.getPublicKey();
}
Jared Rummler
  • 35,743
  • 18
  • 127
  • 142
chavanNil
  • 177
  • 2
  • 4
0

I don't have the Java code stored at the top of my brain, but some general sanity checks are:

  • is the public certificate you want stored where you want it? In particular, my recollection is that the certificate with the public key and the private key are stored together under a single alias, so the two alias setting you have there seems really odd. Try storing both under the same alias and referencing it in both the private and public key calls.

  • can you get anything else out of the certificate - for example, subject DN or issuer DN are both must-have fields in a certificate. That gives you a good proof that the certificate is being read as expected.

  • in almost any crypto transaction, be very careful with how you read from files and transfer your encoding methods. If you've created your File IO and pulled from it in a weird way, you can corrupt the encoding of the key material. That's a last thing to check - usually Java and JKS haven't been so bad for this, but it happens. On the same vein, be clear about the format of the file - JKS files are different from PKCS 12 files, for example.

bethlakshmi
  • 4,366
  • 20
  • 44
  • I made change to the statement `code` java.security.cert.Certificate cert = keystore.getCertificate(alias); `code` And it displayed public and private key. But the signature part did not execute and the error it is showing is - : java.security.InvalidKeyException: No installed provider supports this key: sun.security.provider.DSAPublicKeyImpl @CoverosGene @aularon – Zack Ef Nov 13 '13 at 07:47
  • Sounds like you've moved forward to a different problem. From the error you report, it sounds like you have a DSA key and no DSA key provider. Sounds like you either need to reconfigure your key pair to use a key algorithm that your current installation supports, or figure out how to install a package that can sign using DSA. – bethlakshmi Nov 13 '13 at 14:33
  • I have checked the key pair properties and it is to support DSA. So, I guess it is the second fault that is of package that can sign using DSA. – Zack Ef Nov 14 '13 at 21:26
  • Yes, or you need to generate a new key pair for something else, get it resigned by the CA (or self sign) and then install and reference it. – bethlakshmi Nov 14 '13 at 23:35