【问题标题】:How to generate PublicKey object from a file in Java如何从 Java 中的文件生成 PublicKey 对象
【发布时间】:2015-11-14 00:39:12
【问题描述】:

我有一个包含公共 RSA 密钥的文件(使用 ssh-keygen 生成)。我想读取文件并生成一个PublicKey 对象。

在此之前我转换了文件,因为读取原始文件似乎是不可能的:

# http://unix.stackexchange.com/questions/220354/how-to-convert-public-key-from-pem-to-der-format/220356#220356
ssh-keygen -f ~/.ssh/id_rsa.pub -e -m PEM > ~/.ssh/id_rsa.pub.pem
openssl rsa -RSAPublicKey_in -in ~/.ssh/id_rsa.pub.pem -inform PEM -outform DER -out ~/.ssh/id_rsa.pub.der -RSAPublicKey_out

Java - Encrypt String with existing public key file我定义了函数readFileBytes

public static byte[] readFileBytes(String filename) throws IOException {
    Path path = Paths.get(System.getProperty("user.home") + filename);
    return Files.readAllBytes(path);
}

现在我想读取文件并生成 PublicKey 对象,但我找不到这样做的方法; java.security.spec.RSAPublicKeySpec 没有提供合适的构造函数,java.security.spec.X509EncodedKeySpec 抛出错误 java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence

//RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(readFileBytes("/.ssh/id_rsa.pub.der"));
// No fitting construktor

X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(readFileBytes("/.ssh/id_rsa.pub.der"));
// Gives: "algid parse error, not a sequence"

【问题讨论】:

    标签: java encryption rsa bouncycastle public-key-encryption


    【解决方案1】:

    创建 RSA 私钥

    openssl genrsa -out rsaprivkey.pem 1024

    生成 DER 格式的公钥。

    openssl rsa -in rsaprivkey.pem -pubout -outform DER -out rsapubkey.der

    我们使用此代码从 X.509 证书中提取公钥 RSA 或 DSA。

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.security.KeyFactory;
    import java.security.NoSuchAlgorithmException;
    import java.security.PublicKey;
    import java.security.cert.CertificateException;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.X509EncodedKeySpec;
    
    /**
     * This class is capable of extracting a public key from a X.509 certficate 
     * and returning the PublicKey representation from a referenced byte array.
     * 
     */
    public class ExtractPublicKey {
    
      // Certificate Filename (Including Path Info)
      private static final String certFilename = "cacert.pem";
    
      // Public Key Filename (Including Path Info)
      private static final String pubKeyFilename = "rsapublic.key";
    
      public static PublicKey generatePublicKey(byte[] encodedKey)
          throws NoSuchAlgorithmException, InvalidKeySpecException {
    
        X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(encodedKey);
        boolean isSupportedKey = false;
        KeyFactory factory;
        PublicKey retKey = null;
    
        //first try the DSA alg
        try {
          factory = KeyFactory.getInstance("DSA");
          retKey = factory.generatePublic(pubSpec);
          isSupportedKey = true;
        } catch (InvalidKeySpecException e) {
          System.out.println("Could not create DSA Public Key: " + e.toString());      
        }
    
        //if DSA didnt work, then try RSA    
        if (!isSupportedKey) {
          try {
            factory = KeyFactory.getInstance("RSA");
            retKey = factory.generatePublic(pubSpec);
            isSupportedKey = true;
          } catch (InvalidKeySpecException e) {
            System.out.println("Could not create RSA Public Key: " + e.toString());
          }      
        }
    
        // if not DSA or RSA
        if (!isSupportedKey) {
          throw new InvalidKeySpecException("Unsupported key spec: Not RSA or DSA");
        }
    
        return retKey;
      }   
    
    }
    

    【讨论】:

    • 我们为 Google 应用程序提供 SSO,并使用上述代码从 x509 中提取公钥。它是 Java 代码而不是 Google 应用程序特定代码这在我们的其他项目中也可以正常工作。如果您想了解更多信息如何提取或如何生成密钥检查此链接。 developers.google.com/google-apps/help/articles/…
    【解决方案2】:

    我有一个项目,其中需要 (RSA) 加密,这就是我在给定 publicKeybyte 数组的情况下重建 publicKey 的方式,该数组刚刚从文件中读取。

    public PublicKey reconstruct_public_key(String algorithm, byte[] pub_key) {
        PublicKey public_key = null;
    
        try {
            KeyFactory kf = KeyFactory.getInstance(algorithm);
            EncodedKeySpec pub_key_spec = new X509EncodedKeySpec(pub_key);
            public_key = kf.generatePublic(pub_key_spec);
        } catch(NoSuchAlgorithmException e) {
            System.out.println("Could not reconstruct the public key, the given algorithm oculd not be found.");
        } catch(InvalidKeySpecException e) {
            System.out.println("Could not reconstruct the public key");
        }
    
        return public_key;
    }
    

    然后你可以调用类似于这个调用的过程,reconstruct_public_key("RSA", readFileBytes("path/to/your/publicKey/file"));

    编辑:我尝试自己做(将公钥写入文件,读取该文件并重建密钥)。这有效:

    public static void main(String args[]) {
        String path = "./pub_key_test.txt";
    
        // Generate a keypair to write to file
        KeyPair kp = generate_key();
        PublicKey pub_key = kp.getPublic();
        File file = new File(path);
    
        try {
            // Write to file
            file.createNewFile();
            FileOutputStream out = new FileOutputStream(path);
    
            out.write(pub_key.getEncoded()); // Write public key to the file
            out.close();
    
            // Read from file
            FileInputStream in = new FileInputStream(path);
            byte[] pub_key_arr = new byte[in.available()];
            in.read(pub_key_arr, 0, in.available());
            in.close();
    
            // Reconstruct public key
            PublicKey reconstructed_pub_key = reconstruct_public_key("RSA", pub_key_arr);
        } catch(IOException e) {
            System.out.println("Could not open the file : " + e.getStackTrace());
        }
    }
    

    这是generate_key 程序:

    public KeyPair generate_key() {
        while(true) { // Else the compiler will complain that this procedure does not always return a "KeyPair"
            try {
                final KeyPairGenerator key_generator = KeyPairGenerator.getInstance("RSA");
                key_generator.initialize(2048); // Keys of 2048 bits (minimum key length for RSA keys) are safe enough (according to the slides 128bit keys > 16 years to brute force it)
    
                final KeyPair keys = key_generator.generateKeyPair();
                return keys;
            } catch(NoSuchAlgorithmException e) {
                System.out.println("The given encryption algorithm (RSA) does not exist. -- generate_key() - Cryptography.");
            }
        }
    }
    

    如果您对此进行测试,您将看到publicKey 已成功重构。

    编辑:我自己尝试过,使用ssh-keygen 工具。这就是我所做的:

    • 首先我生成了一个 RSA 私钥(.PEM 格式)
    • 将公钥部分输出为.DER格式,以便Java使用。

    这就是我进行转换的方式,这与您的有点不同:

    openssl rsa -in private_key_file.pem -pubout -outform DER -out java_readable_file.der
    

    我的文件读起来像here,和你的差别不大。我对此进行了测试,Java 成功地重建了公钥。

    【讨论】:

    • 好吧,我总是得到“无法重建公钥” - 但我仍然不知道为什么无法重建公钥,......也许在转换过程中出了点问题-过程,但是什么?如果有一种方法可以直接读取ssh-keygen 生成的文件,而不需要在终端中手动转换,那也很棒。
    • @Edward 我编辑了我的答案以回答您的问题。
    • 我尝试读取使用工具ssh-keygen(通常在~/.ssh/id_rsa.pub)生成的现有密钥。它们似乎采用PEM-格式。我设法从~/.ssh/id_rsa 读取了私钥,但读取公钥不起作用。使用ssh-keygen 生成的文件似乎确实存在问题(希望现在清楚)。 [+1 为您的示例]
    猜你喜欢
    • 2016-11-13
    • 1970-01-01
    • 2019-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多