Java:使用 KeyStore 保存 DES 密钥

Java: Saving a DES Key Using KeyStore

我正在尝试使用 KeyStore 将 DES 密钥保存到文件中供以后使用。这是我的代码:

    // 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();
        }
    }

但是,当我尝试使用 KeyStore.setEntry() 方法时,我收到错误消息,指出 DES 是一种无法识别的算法。异常堆栈在这里:

    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

在调试时我将问题缩小到函数 java.base/sun.security.x509.AlgorithmID.get(AlgorithmID.java:448)

这是 AlgorithmID class 中 algOID(algname) 的包装函数。它本质上只是一系列 if 语句,用于检查用于生成要保存的密钥的算法的名称。此处包含此方法的一个片段:

// 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 是一种对称密钥算法,应与 AES 一起包含在密钥算法部分,但未列出。我知道 DES 是一种较旧的算法,但他们真的取消了对它的支持吗?

我将不胜感激任何帮助,无论是使密钥库工作还是其他将 DES 密钥写入文件以便稍后可以再次读取的方法。谢谢!

您使用的是哪个 JDK 版本?

众所周知,DES 算法很弱,因此它已从 JDK14 中的许多地方删除:https://bugs.openjdk.java.net/browse/JDK-8233607

简而言之,你不应该使用它

就是说,[我尝试用 Clojure 重写您的示例][1](抱歉,这些天我不使用 Java)并且效果很好 - 使用 Java 17.0. 2:

(ns clojure-experiments.security.keystore
  (:import (javax.crypto KeyGenerator)
           (java.security KeyStore
                          KeyStore$SecretKeyEntry
                          KeyStore$PasswordProtection)))

;; 

(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.