javacryptographykeystoredes

Java: Saving a DES Key Using KeyStore


I am trying to save a DES key to a file using KeyStore for later use. Here is my code:

    // Generate a DES key.
    KeyGenerator kg = KeyGenerator.getInstance("DES");
    SecretKey k = kg.generateKey();

    // Store it in a file.
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null, null);
    char[] pw = "moon".toCharArray();
    KeyStore.SecretKeyEntry sk = new KeyStore.SecretKeyEntry(k);
    ks.setEntry("k1", sk, new KeyStore.PasswordProtection(pw));

    // store away the keystore
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream("DESKey.jks");
        ks.store(fos, pw);
    } finally {
        if (fos != null) {
            fos.close();
        }
    }

However, when I try to use the KeyStore.setEntry() method I receive errors stating that DES is an unrecognized algorithm. The exception stack is here:

    Exception in thread "main" java.security.KeyStoreException: Key protection algorithm not found: java.security.NoSuchAlgorithmException: unrecognized algorithm name: DES 
at java.base/sun.security.pkcs12.PKCS12KeyStore.setKeyEntry(PKCS12KeyStore.java:688)
at java.base/sun.security.pkcs12.PKCS12KeyStore.engineSetEntry(PKCS12KeyStore.java:1423)
at java.base/sun.security.util.KeyStoreDelegator.engineSetEntry(KeyStoreDelegator.java:173) at java.base/java.security.KeyStore.setEntry(KeyStore.java:1591) at CipherClient.main(CipherClient.java:27)
    Caused by: java.security.NoSuchAlgorithmException: unrecognized algorithm name: DES 
at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:448) 
at java.base/sun.security.pkcs12.PKCS12KeyStore.setKeyEntry(PKCS12KeyStore.java:656) 
... 4 more

While debugging I narrowed the issue down to the function java.base/sun.security.x509.AlgorithmID.get(AlgorithmID.java:448)

This is a wrapper function for algOID(algname) in the AlgorithmID class. It is essentially just a series of if statements checking the name of the algorithm used to generate the key being saved. A snippet of this method is included here:

// See if algname is in printable OID ("dot-dot") notation
    if (name.indexOf('.') != -1) {
        if (name.startsWith("OID.")) {
            return new ObjectIdentifier(name.substring("OID.".length()));
        } else {
            return new ObjectIdentifier(name);
        }
    }

    // Digesting algorithms
    if (name.equalsIgnoreCase("MD5")) {
        return AlgorithmId.MD5_oid;
    }
    if (name.equalsIgnoreCase("MD2")) {
        return AlgorithmId.MD2_oid;
    }
    if (name.equalsIgnoreCase("SHA") || name.equalsIgnoreCase("SHA1")
        || name.equalsIgnoreCase("SHA-1")) {
        return AlgorithmId.SHA_oid;
    }
    if (name.equalsIgnoreCase("SHA-256") ||
        name.equalsIgnoreCase("SHA256")) {
        return AlgorithmId.SHA256_oid;
    }
    if (name.equalsIgnoreCase("SHA-384") ||
        name.equalsIgnoreCase("SHA384")) {
        return AlgorithmId.SHA384_oid;
    }
    if (name.equalsIgnoreCase("SHA-512") ||
        name.equalsIgnoreCase("SHA512")) {
        return AlgorithmId.SHA512_oid;
    }
    if (name.equalsIgnoreCase("SHA-224") ||
        name.equalsIgnoreCase("SHA224")) {
        return AlgorithmId.SHA224_oid;
    }
    if (name.equalsIgnoreCase("SHA-512/224") ||
        name.equalsIgnoreCase("SHA512/224")) {
        return AlgorithmId.SHA512_224_oid;
    }
    if (name.equalsIgnoreCase("SHA-512/256") ||
        name.equalsIgnoreCase("SHA512/256")) {
        return AlgorithmId.SHA512_256_oid;
    }
    // Various public key algorithms
    if (name.equalsIgnoreCase("RSA")) {
        return AlgorithmId.RSAEncryption_oid;
    }
    if (name.equalsIgnoreCase("RSASSA-PSS")) {
        return AlgorithmId.RSASSA_PSS_oid;
    }
    if (name.equalsIgnoreCase("RSAES-OAEP")) {
        return AlgorithmId.RSAES_OAEP_oid;
    }
    if (name.equalsIgnoreCase("Diffie-Hellman")
        || name.equalsIgnoreCase("DH")) {
        return AlgorithmId.DH_oid;
    }
    if (name.equalsIgnoreCase("DSA")) {
        return AlgorithmId.DSA_oid;
    }
    if (name.equalsIgnoreCase("EC")) {
        return EC_oid;
    }
    if (name.equalsIgnoreCase("ECDH")) {
        return AlgorithmId.ECDH_oid;
    }

    // Secret key algorithms
    if (name.equalsIgnoreCase("AES")) {
        return AlgorithmId.AES_oid;
    }

    // Common signature types
    if (name.equalsIgnoreCase("MD5withRSA")
        || name.equalsIgnoreCase("MD5/RSA")) {
        return AlgorithmId.md5WithRSAEncryption_oid;
    }
    if (name.equalsIgnoreCase("MD2withRSA")
        || name.equalsIgnoreCase("MD2/RSA")) {
        return AlgorithmId.md2WithRSAEncryption_oid;
    }

DES, a symmetric key algorithm, should be included in the secret key algorithms section along with AES, but it isn't listed. I understand DES is an older algorithm, but did they really remove support for it?

I would appreciate any assistance, whether to make the keystore work or other methods of writing the DES key to a file where it can be read again later. Thanks!


Solution

  • Which JDK version you are using?

    The DES algorithm is known to be weak and as such it has been removed from a bunch of places in JDK 14: https://bugs.openjdk.java.net/browse/JDK-8233607

    In short, you shouldn't use it.

    That said, [I tried to rewrite your example in Clojure][1] (sorry, I don't use Java these days) and it worked well - using Java 17.0.2:

    (ns clojure-experiments.security.keystore
      (:import (javax.crypto KeyGenerator)
               (java.security KeyStore
                              KeyStore$SecretKeyEntry
                              KeyStore$PasswordProtection)))
    
    ;; https://stackoverflow.com/questions/71754646/java-saving-a-des-key-using-keystore
    
    (comment
      ;; https://docs.oracle.com/javase/7/docs/api/javax/crypto/KeyGenerator.html
      (def my-kg (KeyGenerator/getInstance "DES"))
      (def my-key (.generateKey my-kg))
    
      ;; https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.html
      (def my-keystore (KeyStore/getInstance (KeyStore/getDefaultType)))
      (.load my-keystore nil nil) ; passing nil input stream to create a new KeyStore
    
      (def my-password (char-array "changeit"))
    
      ;; save the key in the keystore
      (def my-key-entry (KeyStore$SecretKeyEntry. my-key))
      (def my-key-alias "myKeyAlias")
      (def my-key-password (KeyStore$PasswordProtection. my-password))
      (.setEntry my-keystore my-key-alias my-key-entry my-key-password)
    
      ;; save the keystore
      (def keystore-path "/Users/jumar/my-key-store.jks")
      (.store my-keystore (java.io.FileOutputStream. keystore-path) my-password)
    
      ;; now read the key from the saved keystore
      (def read-keystore (KeyStore/getInstance (KeyStore/getDefaultType)))
      (.load read-keystore (java.io.FileInputStream. keystore-path) my-password)
      (.getEntry read-keystore my-key-alias my-key-password)
      ;; => #object[java.security.KeyStore$SecretKeyEntry 0x58b80b62 "Secret key entry with algorithm DES/CBC"]
    
    ,)
    
    And btw. I also copy-pasted your code to IntelliJ to try it quickly and it all worked - again, using OpenJDK 17.