encryption - Java - 如何修复 InvalidKeyException : Wrong algorithm type - RSA

标签 encryption rsa public-key key java

我得到了一个私有(private) JKS 文件和一个密码文件。我看到互联网,写了一个java程序,它将私钥作为字符串打印出来。

同样,我得到了一个扩展名为 .cer 的公钥文件。同样,我也以字符串形式打印出该文件的内容

我的想法是编写一个java程序,它有两种方法加密和解密。我写了所有的东西。但是当我尝试使用加密和解密函数时,我看到 InvalidKeyException:算法类型错误。我从 jks 文件中打印出了算法,我将其视为 RSA。在公共(public)证书中,算法被打印为 SHA1withRSA

这是我的程序

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import org.apache.commons.codec.binary.Base64;

public class ExtractKeys {

    private static String privateKeyalgorithm = "";
    private static String publicKeyAlgorithm = "";

    public static void main(String args[]) throws Exception {

        String publicCertificate = "C:\\QA\\keys\\sis.cer";

        String privateKeyStore = "C:\\QA\\keys\\sis.jks";
        String privateKeyStorePassword = "sis";
        String alias = "sis";
        String aliasPassword = "sis";

        ExtractKeys myep = new ExtractKeys();

        myep.printPrivateKey(privateKeyStore, privateKeyStorePassword, alias, aliasPassword);
        myep.printPublicKey(publicCertificate);

    }

    public String printPublicKey(String source) throws Exception {
        X509Certificate cert = null;
        InputStream fis = null;
        ByteArrayInputStream bais = null;
        Base64 encoder = new Base64(64);

        fis = new FileInputStream(source);
        byte value[] = new byte[fis.available()];
        fis.read(value);
        bais = new ByteArrayInputStream(value);
        java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509");
        cert = (X509Certificate) cf.generateCertificate(bais);

        String cert_begin = "-----BEGIN CERTIFICATE-----\n";
        String end_cert = "-----END CERTIFICATE-----";
        publicKeyAlgorithm = cert.getSigAlgName();
        byte[] derCert = cert.getEncoded();
        String pemCertPre = new String(encoder.encode(derCert));
        String pemCert = cert_begin + pemCertPre + end_cert;
        System.out.println(pemCert);
        System.out.println(publicKeyAlgorithm);
        return pemCert;

    }

    public String printPrivateKey(String fileName, String keystorepass, String aliasName, String aliaspass) throws Exception {

        KeyStore ks = KeyStore.getInstance("JKS");
        Base64 encoder = new Base64(64);

        char[] keyStorePassPhrase = keystorepass.toCharArray();

        File certificateFile = new File(fileName);

        ks.load(new FileInputStream(certificateFile), keyStorePassPhrase);

        char[] aliasPassPhrase = aliaspass.toCharArray();

        KeyPair kp = getPrivateKey(ks, aliasName, aliasPassPhrase);

        PrivateKey privKey = kp.getPrivate();

        privateKeyalgorithm = privKey.getAlgorithm();

        String b64 = encoder.encodeAsString(privKey.getEncoded());

        System.out.println("-----BEGIN PRIVATE KEY-----");

        System.out.println(b64);

        System.out.println("-----END PRIVATE KEY-----");

        System.out.println(privateKeyalgorithm);

        return b64;

    }

    private KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) {

        try {

            // Get private key

            Key key = keystore.getKey(alias, password);

            if (key instanceof PrivateKey) {

                // Get certificate of public key

                Certificate cert = keystore.getCertificate(alias);

                // Get public key

                PublicKey publicKey = cert.getPublicKey();

                // Return a key pair

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

            }

        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();

        } catch (KeyStoreException e) {
            e.printStackTrace();

        }

        return null;

    }

    public String returnPrivateKeyAlgo() {
        return privateKeyalgorithm;
    }

    public String returnPublicKeyAlgo() {
        return publicKeyAlgorithm;
    }

输出是公钥、私钥及其算法的字符串

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZehr60/61+4ao
8Bigkamzt3RJZcY9ggE4YuQ1ALY8pDXARDQ3OqPohySw4y+Ebrk4y/Rwzm21mhaU
jC33BNSNUetRAgBDU+/nT3/gRZdIeJRfFdiDtl/Ms2LYxo41nYTeGJEqoW3fivI2
cUG/tDKSPjaoGwnz/kVNIsZXJEcZCAfdIg1UH0wBeQ5qLR4rsseE0I7vVAfvDsMl
-----END PRIVATE KEY-----
RSA
-----BEGIN CERTIFICATE-----
MIIGiTCCBXGgAwIBAgIKYMbAHgAAAAUgKzANBgkqhkiG9w0BAQUFADB5MRMwEQYK
CZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGc2Nod2FiMRYwFAYKCZIm
-----END CERTIFICATE-----
SHA1withRSA

现在我得到了作为字符串值的公钥和私钥。以及他们的算法。

接下来我编写了另一个示例程序。其中有两个方法。加密和解密也接受这些 key 。

所以理想情况下,我将公钥传递给加密方法,将私钥传递给解密方法

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import **.CryptoService;
import **.CryptoServiceException;


public class CryptoServiceImpl implements CryptoService {

public byte[] encrypt(byte[] data, String key) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            Base64 decoder = new Base64(64);

            // decode the base64 encoded string
            byte[] decodedKey = decoder.decode(key);
            // rebuild key using SecretKeySpec
            final SecretKeySpec originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "RSA");

            cipher.init(Cipher.ENCRYPT_MODE, originalKey);
            final String encryptedString = Base64.encodeBase64String(cipher.doFinal(data));
            return encryptedString.getBytes();
        } catch (Exception e) {
            throw new CryptoServiceException("Cannot encrypt data using key '", e);
        }

    }

    public byte[] decrypt(byte[] data, String key) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            Base64 decoder = new Base64(64);

            // decode the base64 encoded string
            byte[] decodedKey = decoder.decode(key);
            // rebuild key using SecretKeySpec
            final SecretKeySpec originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "RSA");

            cipher.init(Cipher.DECRYPT_MODE, originalKey);
            final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(new String(data))));
            return decryptedString.getBytes();
        } catch (Exception e) {
            throw new CryptoServiceException("Cannot decrypt data using key '", e);

        }
    }
}

现在我得到了一个 Junit,它在执行时会抛出此异常

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import junit.framework.Assert;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import **.CryptoServiceImpl;

public class TestCryptoService {

    String privateKeyStore = "C:\\QA\\keys\\sis.jks";
    String privateKeyStorePassword = "sistest";
    String alias = "sis";
    String aliasPassword = "sistest";
    static ExtractKeys myep = new ExtractKeys();
    CryptoService service = new CryptoServiceImpl();
    String publicCertificate = "C:\\QA\\keys\\sis.cer";;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {
    }

@Test
    public void testCryptoServiceForAESWithAsymmetricKeys() throws Exception {

        String publicKey = myep.printPublicKey(publicCertificate);

        byte[] encryptedValue = service.encrypt(new String("abcd").getBytes(), publicKey);
        System.out.println(new String(encryptedValue));

        String privateKey = myep.printPrivateKey(privateKeyStore, privateKeyStorePassword, alias, aliasPassword);
        byte[] decryptedValue = service.decrypt(encryptedValue, privateKey);

        System.out.println(new String(decryptedValue));
        Assert.assertEquals("abcd", new String(decryptedValue));
    }
}

这是一个异常(exception)

*.CryptoServiceException: Cannot encrypt data using key '
    at *.impl.CryptoServiceImpl.encrypt(CryptoServiceImpl.java:59)
    at *.TestCryptoService.testCryptoServiceForAESWithAsymmetricKeys(TestCryptoService.java:101)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56)
    at java.lang.reflect.Method.invoke(Method.java:620)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    	at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.security.InvalidKeyException: Wrong algorithm type
    at com.ibm.crypto.provider.RSAKeyFactory.engineTranslateKey(Unknown Source)
    at com.ibm.crypto.provider.RSAKeyFactory.toRSAKey(Unknown Source)
    at com.ibm.crypto.provider.RSACipher.engineGetKeySize(Unknown Source)
    at javax.crypto.Cipher.b(Unknown Source)
    at javax.crypto.Cipher.a(Unknown Source)
    at javax.crypto.Cipher.a(Unknown Source)
    at javax.crypto.Cipher.a(Unknown Source)
    at javax.crypto.Cipher.init(Unknown Source)
    at javax.crypto.Cipher.init(Unknown Source)
    at *.impl.CryptoServiceImpl.encrypt(CryptoServiceImpl.java:55)
    ... 28 more

最佳答案

证书不是公钥,尽管它包含公钥;更重要的是,像 RSA 这样的公钥加密(又名非对称)方案的私钥、公钥或证书都不是对称 key 。 Java SecretKeySpec 仅适用于与对称算法(例如 AES)一起使用的对称 key 。

您需要使用 java.security.PrivateKeyjava.security.PublicKey 的子类型来表示 RSA key 。您执行此操作的方法是使用 RSA 的 KeyFactoryCertificateFactory (就像您已经做过的那样)。

对于 PKCS#8 编码的私钥(即您编码和写出的私钥),将其从 PEM/base64 解码为 byte[],然后:

PKCS8EncodedKeySpec spec = new PKCSEncodedKeySpec (bytearray);
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privkey = factory.generatePrivate (spec);
// use privkey in a Cipher.getInstance("RSA") to decrypt or sign

对于证书,基本上要做你已经做过的事情:

InputStream is = /* something that returns contents of cert file */;
// you *can* decode from PEM/base64 first, but you don't need to;
// CertificateFactory-X.509 handles both
CertificateFactory cf = CertificateFactory.getInstance ("X.509");
Certificate cert = cf.generateCertificate (is);
// use cert.getPublicKey() for RSA encrypt or verify

旁白:证书的 sigAlgName 不会透露证书中的 key 。签名 SHA1withRSA 的证书完全有可能包含不能用于 RSA 的 DSA 或 ECC key ,反之,包含完美 RSA key 的证书也可能使用不同的 RSA 变体(如 SHA256withRSA)或完全不同的算法(如 sha1-DSA 或 sha2-ECDSA)。

另请注意:您正在显式导入java.security.cert.Certificate;那挺好的。 JRE 还有一个较旧且已弃用的 java.security.Certificate,您不应该使用它,但如果您只是说 Certificate,某些 IDE(例如我的)可能会感到困惑并给您提供信息错了。

关于encryption - Java - 如何修复 InvalidKeyException : Wrong algorithm type - RSA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33272130/

相关文章:

JAVA AES 256 和公钥加密

Git SSH 身份验证

c# - 如何在 C# .NET 中像 Java 代码一样加密

objective-c - 非托管 <SecKey> 不可转换为 SecKey

java - 在 Java 中解密 linux encfs(标准配置,192 位 aes)

java - 使用JAVA进行RSA加密/解密

c++ - OpenSSL:我使用存储在外部文件中的 RSA key 对文件进行加密和解密,但有时我的程序会崩溃

symfony - 在 Heroku 上使用 LexikJWTAuthenticationBundle 部署 Symfony 应用程序

c# - 保护共享主机上的连接字符串

java - 基于 Java 代码的 Crypt::CBC 的 Perl 问题