From cce99e57e19833e6783ba344864999ec8d475ba5 Mon Sep 17 00:00:00 2001 From: Prajwal Kumaraswamy Date: Thu, 22 Apr 2021 13:15:55 +0000 Subject: [PATCH] 8023980: JCE doesn't provide any class to handle RSA private key in PKCS#1 Backport-of: 68cf65d284a73f5c5229d30ca642bba9585095f3 --- .../sun/security/rsa/RSAKeyFactory.java | 49 +++--- .../security/rsa/RSAPrivateCrtKeyImpl.java | 134 ++++++++++------ .../sun/security/rsa/RSAPublicKeyImpl.java | 67 +++++--- .../sun/security/pkcs11/P11RSAKeyFactory.java | 31 ++-- .../security/pkcs11/rsa/TestKeyFactory.java | 136 ++++++++++++++-- test/jdk/sun/security/rsa/TestKeyFactory.java | 149 +++++++++++++++--- 6 files changed, 407 insertions(+), 159 deletions(-) diff --git a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java index cb68c2532d5..ede131b0fef 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,13 +43,15 @@ * between the following: * * For public keys: - * . PublicKey with an X.509 encoding + * . RSA PublicKey with an X.509 encoding + * . RSA PublicKey with an PKCS#1 encoding * . RSAPublicKey * . RSAPublicKeySpec * . X509EncodedKeySpec * * For private keys: - * . PrivateKey with a PKCS#8 encoding + * . RSA PrivateKey with a PKCS#8 encoding + * . RSA PrivateKey with a PKCS#1 encoding * . RSAPrivateKey * . RSAPrivateCrtKey * . RSAPrivateKeySpec @@ -95,8 +97,8 @@ static RSAKeyFactory getInstance(KeyType type) { return new RSAKeyFactory(type); } - // Internal utility method for checking key algorithm - private static void checkKeyAlgo(Key key, String expectedAlg) + // pkg-private utility method for checking key algorithm + static void checkKeyAlgo(Key key, String expectedAlg) throws InvalidKeyException { String keyAlg = key.getAlgorithm(); if (keyAlg == null || !(keyAlg.equalsIgnoreCase(expectedAlg))) { @@ -265,14 +267,10 @@ private PublicKey translatePublicKey(PublicKey key) // catch providers that incorrectly implement RSAPublicKey throw new InvalidKeyException("Invalid key", e); } - } else if ("X.509".equals(key.getFormat())) { - RSAPublicKey translated = new RSAPublicKeyImpl(key.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(translated, type.keyAlgo); - return translated; } else { - throw new InvalidKeyException("Public keys must be instance " - + "of RSAPublicKey or have X.509 encoding"); + // create new key based on the format and encoding of current 'key' + return RSAPublicKeyImpl.newKey(type, key.getFormat(), + key.getEncoded()); } } @@ -309,15 +307,9 @@ private PrivateKey translatePrivateKey(PrivateKey key) // catch providers that incorrectly implement RSAPrivateKey throw new InvalidKeyException("Invalid key", e); } - } else if ("PKCS#8".equals(key.getFormat())) { - RSAPrivateKey translated = - RSAPrivateCrtKeyImpl.newKey(key.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(translated, type.keyAlgo); - return translated; } else { - throw new InvalidKeyException("Private keys must be instance " - + "of RSAPrivate(Crt)Key or have PKCS#8 encoding"); + return RSAPrivateCrtKeyImpl.newKey(type, key.getFormat(), + key.getEncoded()); } } @@ -325,11 +317,8 @@ private PrivateKey translatePrivateKey(PrivateKey key) private PublicKey generatePublic(KeySpec keySpec) throws GeneralSecurityException { if (keySpec instanceof X509EncodedKeySpec) { - X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec; - RSAPublicKey generated = new RSAPublicKeyImpl(x509Spec.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(generated, type.keyAlgo); - return generated; + return RSAPublicKeyImpl.newKey(type, "X.509", + ((X509EncodedKeySpec)keySpec).getEncoded()); } else if (keySpec instanceof RSAPublicKeySpec) { RSAPublicKeySpec rsaSpec = (RSAPublicKeySpec)keySpec; try { @@ -351,11 +340,8 @@ private PublicKey generatePublic(KeySpec keySpec) private PrivateKey generatePrivate(KeySpec keySpec) throws GeneralSecurityException { if (keySpec instanceof PKCS8EncodedKeySpec) { - PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec; - RSAPrivateKey generated = RSAPrivateCrtKeyImpl.newKey(pkcsSpec.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(generated, type.keyAlgo); - return generated; + return RSAPrivateCrtKeyImpl.newKey(type, "PKCS#8", + ((PKCS8EncodedKeySpec)keySpec).getEncoded()); } else if (keySpec instanceof RSAPrivateCrtKeySpec) { RSAPrivateCrtKeySpec rsaSpec = (RSAPrivateCrtKeySpec)keySpec; try { @@ -395,7 +381,8 @@ protected T engineGetKeySpec(Key key, Class keySpec) try { // convert key to one of our keys // this also verifies that the key is a valid RSA key and ensures - // that the encoding is X.509/PKCS#8 for public/private keys + // that the encoding is X.509/PKCS#8 or PKCS#1 for public/private + // keys key = engineTranslateKey(key); } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java index 368d451e80a..79441657082 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,30 +74,52 @@ public final class RSAPrivateCrtKeyImpl private transient AlgorithmParameterSpec keyParams; /** - * Generate a new key from its encoding. Returns a CRT key if possible - * and a non-CRT key otherwise. Used by RSAKeyFactory. + * Generate a new RSAPrivate(Crt)Key from the specified type, + * format and encoding. Returns a CRT key if possible and a non-CRT + * key otherwise. + * Also used by SunPKCS11 provider. */ - public static RSAPrivateKey newKey(byte[] encoded) - throws InvalidKeyException { + public static RSAPrivateKey newKey(KeyType type, String format, + byte[] encoded) throws InvalidKeyException { if (encoded == null || encoded.length == 0) { throw new InvalidKeyException("Missing key encoding"); } - RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded); - // check all CRT-specific components are available, if any one - // missing, return a non-CRT key instead - if ((key.getPublicExponent().signum() == 0) || - (key.getPrimeExponentP().signum() == 0) || - (key.getPrimeExponentQ().signum() == 0) || - (key.getPrimeP().signum() == 0) || - (key.getPrimeQ().signum() == 0) || - (key.getCrtCoefficient().signum() == 0)) { - return new RSAPrivateKeyImpl( - key.type, key.keyParams, - key.getModulus(), - key.getPrivateExponent() - ); - } else { - return key; + switch (format) { + case "PKCS#8": + RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded); + RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); + // check all CRT-specific components are available, if any one + // missing, return a non-CRT key instead + if ((key.getPublicExponent().signum() == 0) || + (key.getPrimeExponentP().signum() == 0) || + (key.getPrimeExponentQ().signum() == 0) || + (key.getPrimeP().signum() == 0) || + (key.getPrimeQ().signum() == 0) || + (key.getCrtCoefficient().signum() == 0)) { + return new RSAPrivateKeyImpl(key.type, key.keyParams, + key.getModulus(), key.getPrivateExponent()); + } else { + return key; + } + case "PKCS#1": + try { + BigInteger[] comps = parseASN1(encoded); + if ((comps[1].signum() == 0) || (comps[3].signum() == 0) || + (comps[4].signum() == 0) || (comps[5].signum() == 0) || + (comps[6].signum() == 0) || (comps[7].signum() == 0)) { + return new RSAPrivateKeyImpl(type, null, comps[0], + comps[2]); + } else { + return new RSAPrivateCrtKeyImpl(type, null, comps[0], + comps[1], comps[2], comps[3], comps[4], comps[5], + comps[6], comps[7]); + } + } catch (IOException ioe) { + throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe); + } + default: + throw new InvalidKeyException("Unsupported RSA Private(Crt)Key " + + "format: " + format); } } @@ -126,7 +148,7 @@ public static RSAPrivateKey newKey(KeyType type, /** * Construct a key from its encoding. Called from newKey above. */ - RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { + private RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { super(encoded); parseKeyBits(); RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); @@ -258,37 +280,47 @@ public String toString() { + "\n modulus: " + n + "\n private exponent: " + d; } + // utility method for parsing DER encoding of RSA private keys in PKCS#1 + // format as defined in RFC 8017 Appendix A.1.2, i.e. SEQ of version, n, + // e, d, p, q, pe, qe, and coeff, and return the parsed components. + private static BigInteger[] parseASN1(byte[] raw) throws IOException { + DerValue derValue = new DerValue(raw); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + int version = derValue.data.getInteger(); + if (version != 0) { + throw new IOException("Version must be 0"); + } + + BigInteger[] result = new BigInteger[8]; // n, e, d, p, q, pe, qe, coeff + /* + * Some implementations do not correctly encode ASN.1 INTEGER values + * in 2's complement format, resulting in a negative integer when + * decoded. Correct the error by converting it to a positive integer. + * + * See CR 6255949 + */ + for (int i = 0; i < result.length; i++) { + result[i] = derValue.data.getPositiveBigInteger(); + } + if (derValue.data.available() != 0) { + throw new IOException("Extra data available"); + } + return result; + } + private void parseKeyBits() throws InvalidKeyException { try { - DerInputStream in = new DerInputStream(key); - DerValue derValue = in.getDerValue(); - if (derValue.tag != DerValue.tag_Sequence) { - throw new IOException("Not a SEQUENCE"); - } - DerInputStream data = derValue.data; - int version = data.getInteger(); - if (version != 0) { - throw new IOException("Version must be 0"); - } - - /* - * Some implementations do not correctly encode ASN.1 INTEGER values - * in 2's complement format, resulting in a negative integer when - * decoded. Correct the error by converting it to a positive integer. - * - * See CR 6255949 - */ - n = data.getPositiveBigInteger(); - e = data.getPositiveBigInteger(); - d = data.getPositiveBigInteger(); - p = data.getPositiveBigInteger(); - q = data.getPositiveBigInteger(); - pe = data.getPositiveBigInteger(); - qe = data.getPositiveBigInteger(); - coeff = data.getPositiveBigInteger(); - if (derValue.data.available() != 0) { - throw new IOException("Extra data available"); - } + BigInteger[] comps = parseASN1(key); + n = comps[0]; + e = comps[1]; + d = comps[2]; + p = comps[3]; + q = comps[4]; + pe = comps[5]; + qe = comps[6]; + coeff = comps[7]; } catch (IOException e) { throw new InvalidKeyException("Invalid RSA private key", e); } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java index 8d0a69b1fe1..d5002c15000 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,17 +66,36 @@ public final class RSAPublicKeyImpl extends X509Key implements RSAPublicKey { private transient AlgorithmParameterSpec keyParams; /** - * Generate a new RSAPublicKey from the specified encoding. - * Used by SunPKCS11 provider. + * Generate a new RSAPublicKey from the specified type, format, and + * encoding. + * Also used by SunPKCS11 provider. */ - public static RSAPublicKey newKey(byte[] encoded) - throws InvalidKeyException { - return new RSAPublicKeyImpl(encoded); + public static RSAPublicKey newKey(KeyType type, String format, + byte[] encoded) throws InvalidKeyException { + RSAPublicKey key; + switch (format) { + case "X.509": + key = new RSAPublicKeyImpl(encoded); + RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); + break; + case "PKCS#1": + try { + BigInteger[] comps = parseASN1(encoded); + key = new RSAPublicKeyImpl(type, null, comps[0], comps[1]); + } catch (IOException ioe) { + throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe); + } + break; + default: + throw new InvalidKeyException("Unsupported RSA PublicKey format: " + + format); + } + return key; } /** * Generate a new RSAPublicKey from the specified type and components. - * Used by SunPKCS11 provider. + * Also used by SunPKCS11 provider. */ public static RSAPublicKey newKey(KeyType type, AlgorithmParameterSpec params, BigInteger n, BigInteger e) @@ -123,9 +142,9 @@ public static RSAPublicKey newKey(KeyType type, } /** - * Construct a key from its encoding. Used by RSAKeyFactory. + * Construct a key from its encoding. */ - RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { + private RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { if (encoded == null || encoded.length == 0) { throw new InvalidKeyException("Missing key encoding"); } @@ -181,22 +200,30 @@ public AlgorithmParameterSpec getParams() { return keyParams; } + // utility method for parsing DER encoding of RSA public keys in PKCS#1 + // format as defined in RFC 8017 Appendix A.1.1, i.e. SEQ of n and e. + private static BigInteger[] parseASN1(byte[] raw) throws IOException { + DerValue derValue = new DerValue(raw); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + BigInteger[] result = new BigInteger[2]; // n, e + result[0] = derValue.data.getPositiveBigInteger(); + result[1] = derValue.data.getPositiveBigInteger(); + if (derValue.data.available() != 0) { + throw new IOException("Extra data available"); + } + return result; + } + /** * Parse the key. Called by X509Key. */ protected void parseKeyBits() throws InvalidKeyException { try { - DerInputStream in = new DerInputStream(getKey().toByteArray()); - DerValue derValue = in.getDerValue(); - if (derValue.tag != DerValue.tag_Sequence) { - throw new IOException("Not a SEQUENCE"); - } - DerInputStream data = derValue.data; - n = data.getPositiveBigInteger(); - e = data.getPositiveBigInteger(); - if (derValue.data.available() != 0) { - throw new IOException("Extra data available"); - } + BigInteger[] comps = parseASN1(getKey().toByteArray()); + n = comps[0]; + e = comps[1]; } catch (IOException e) { throw new InvalidKeyException("Invalid RSA public key", e); } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java index 88e03e85275..b07be4082ab 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,8 @@ import java.security.spec.*; import sun.security.rsa.RSAPublicKeyImpl; +import sun.security.rsa.RSAPrivateCrtKeyImpl; +import sun.security.rsa.RSAUtil.KeyType; import static sun.security.pkcs11.TemplateManager.*; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; @@ -58,14 +60,11 @@ PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException { rsaKey.getModulus(), rsaKey.getPublicExponent() ); - } else if ("X.509".equals(key.getFormat())) { + } else { // let SunRsaSign provider parse for us, then recurse - byte[] encoded = key.getEncoded(); - key = RSAPublicKeyImpl.newKey(encoded); + key = RSAPublicKeyImpl.newKey(KeyType.RSA, key.getFormat(), + key.getEncoded()); return implTranslatePublicKey(key); - } else { - throw new InvalidKeyException("PublicKey must be instance " - + "of RSAPublicKey or have X.509 encoding"); } } catch (PKCS11Exception e) { throw new InvalidKeyException("Could not create RSA public key", e); @@ -93,14 +92,11 @@ PrivateKey implTranslatePrivateKey(PrivateKey key) rsaKey.getModulus(), rsaKey.getPrivateExponent() ); - } else if ("PKCS#8".equals(key.getFormat())) { + } else { // let SunRsaSign provider parse for us, then recurse - byte[] encoded = key.getEncoded(); - key = sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(encoded); + key = RSAPrivateCrtKeyImpl.newKey(KeyType.RSA, key.getFormat(), + key.getEncoded()); return implTranslatePrivateKey(key); - } else { - throw new InvalidKeyException("Private key must be instance " - + "of RSAPrivate(Crt)Key or have PKCS#8 encoding"); } } catch (PKCS11Exception e) { throw new InvalidKeyException("Could not create RSA private key", e); @@ -113,8 +109,8 @@ protected PublicKey engineGeneratePublic(KeySpec keySpec) token.ensureValid(); if (keySpec instanceof X509EncodedKeySpec) { try { - byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded(); - PublicKey key = RSAPublicKeyImpl.newKey(encoded); + PublicKey key = RSAPublicKeyImpl.newKey(KeyType.RSA, "X.509", + ((X509EncodedKeySpec)keySpec).getEncoded()); return implTranslatePublicKey(key); } catch (InvalidKeyException e) { throw new InvalidKeySpecException @@ -143,9 +139,8 @@ protected PrivateKey engineGeneratePrivate(KeySpec keySpec) token.ensureValid(); if (keySpec instanceof PKCS8EncodedKeySpec) { try { - byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); - PrivateKey key = - sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(encoded); + PrivateKey key = RSAPrivateCrtKeyImpl.newKey(KeyType.RSA, + "PKCS#8", ((PKCS8EncodedKeySpec)keySpec).getEncoded()); return implTranslatePrivateKey(key); } catch (GeneralSecurityException e) { throw new InvalidKeySpecException diff --git a/test/jdk/sun/security/pkcs11/rsa/TestKeyFactory.java b/test/jdk/sun/security/pkcs11/rsa/TestKeyFactory.java index 2b26a52b0c8..065f22e7181 100644 --- a/test/jdk/sun/security/pkcs11/rsa/TestKeyFactory.java +++ b/test/jdk/sun/security/pkcs11/rsa/TestKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4856966 + * @bug 4856966 8023980 * @summary Test KeyFactory of the new RSA provider * @author Andreas Sterbenz * @library /test/lib .. @@ -42,6 +42,84 @@ public class TestKeyFactory extends PKCS11Test { private static final char[] password = "test12".toCharArray(); + private static final String PKCS1_PRIV_STR = + // the BASE64 string between -----BEGIN RSA PRIVATE KEY----- + // and -----END RSA PRIVATE KEY----- + "MIIEowIBAAKCAQEA0OIArlYES4X1XMTLDordtN/XIWFE1wvhl40RsHWM2n99+Stp" + + "CCJCcUb5FJ2/kefj/XRwB6p5IMpIZrHZqC8XXzlX5fpiFaSu2xnk17oWUKoErW27" + + "Stm098pU2RoUxWPKVl+42a8iVp8tijNElBNFALCGi0zXOhcTxMh0q1Wk0UhMJqam" + + "v5YnCKmT4THwwGYn/KeK3M7Qa+o5MoVBHLbeT9LJgEmSluVzIh44Lh6weX0bw72P" + + "8X2praOhbzg2B343MqS/rMLw6On+0i7ccEgp23vX9G5w85q4A5FSIrk4S/pyv5sO" + + "rwjCQKBW1TS0/2iB9zNkFMj5/+h7l2oqTT7sSQIDAQABAoIBADn6sXOynoiUC1IP" + + "sck8lGOTSjSSujfyrVCSsJlJV6qCfuX9va6rS8QDjjnBu531PtxoSHxoPizy2Pvg" + + "W+kKATPGR/am9DjLuFlKq7GRjoYfWyMEdVtGaKvq9ng4fBF6LHyjHz0VFrPyhQJ6" + + "TovHeXzCguYBkzAlnbAeb/vqzs/kABbOuSHVi7DsaixCoEX9zOptFYQw/l8rh68+" + + "UF2bpNNH3jOC1uN3vZtuSwCupqtN+2Mpkx2h04Rk75vWIhrnPeMgmcd3yP4LNZMR" + + "mfaynb63RRzVkNis7+NVk016SQ1oL79mrBvy5rBg3HeCeArwvqZAmOaWsLSWHzCy" + + "zlVlMTECgYEA6JlnMpC956Qi8HX5ye4Hu2ovBdbNGtH/TMkZmColJz9P7CvNkNIb" + + "Od6mvLMydbPHkhdBUDWD4rhiCKHrf5zKju1i24YqWcvuSGotWj4/KQ3+87mLZM+7" + + "daBsJBmSEVB80sgA9ItqSgOyNoNFpiDgFnlszAfb0n9XXEzB/pwSw1UCgYEA5eXI" + + "d+eKugugP+n6CluQfyxfN6WWCzfqWToCTTxPn2i12AiEssXy+kyLjupJVLWSivdo" + + "83wD5LuxFRGc9P+aKQERPhb0AFaxf1llUCXla65/x2So5xjMvtuzgQ0OktPJqJXq" + + "hYGunctsr5rje33+7vlx4xWkrL2PrQWzJabn7SUCgYEAqw3FesY/Ik7u8u+P1xSZ" + + "0xXvptek1oiAu7NYgzLbR9WjrQc5kbsyEojPDg6qmSyxI5q+iYIRj3YRgk+xpJNl" + + "0154SQCNvKPghJiw6aDFSifkytA01tp9/a8QWCwF433RjiFPsoekjvHQ6Y34dofO" + + "xDhf7lwJKPBFCrfYIqocklECgYAIPI9OHHGP8NKw94UJ0fX/WGug5sHVbQ9sWvOy" + + "KLMBlxLMxqFadlUaOpvVZvdxnX++ktajwpGxJDhX9OWWsYGobm1buB7N1E1Prrg+" + + "gt0RWpMhZa3Xeb/8Jorr2Lfo8sWK0LQyTE8hQCSIthfoWL9FeJJn/GKF/dSj8kxU" + + "0QIGMQKBgG/8U/zZ87DzfXS81P1p+CmH474wmou4KD2/zXp/lDR9+dlIUeijlIbU" + + "P6Y5xJvT33Y40giW9irShgDHjZgw0ap11K3b2HzLImdPEaBiENo735rpLs8WLK9H" + + "+yeRbiP2y9To7sTihm9Jrkctzp6sqFtKyye1+S21X1tMz8NGfXen"; + + private static final String PKCS1_PUB_STR = + // the BASE64 string between -----BEGIN RSA PUBLIC KEY----- + // and -----END RSA PUBLIC KEY----- + "MIIBCgKCAQEA0OIArlYES4X1XMTLDordtN/XIWFE1wvhl40RsHWM2n99+StpCCJC" + + "cUb5FJ2/kefj/XRwB6p5IMpIZrHZqC8XXzlX5fpiFaSu2xnk17oWUKoErW27Stm0" + + "98pU2RoUxWPKVl+42a8iVp8tijNElBNFALCGi0zXOhcTxMh0q1Wk0UhMJqamv5Yn" + + "CKmT4THwwGYn/KeK3M7Qa+o5MoVBHLbeT9LJgEmSluVzIh44Lh6weX0bw72P8X2p" + + "raOhbzg2B343MqS/rMLw6On+0i7ccEgp23vX9G5w85q4A5FSIrk4S/pyv5sOrwjC" + + "QKBW1TS0/2iB9zNkFMj5/+h7l2oqTT7sSQIDAQAB"; + + + private static final PrivateKey CUSTOM_PRIV; + private static final PublicKey CUSTOM_PUB; + + static { + byte[] encodedPriv = Base64.getDecoder().decode(PKCS1_PRIV_STR); + CUSTOM_PRIV = new PrivateKey() { + @Override + public String getAlgorithm() { + return "RSA"; + } + @Override + public String getFormat() { + return "PKCS#1"; + } + @Override + public byte[] getEncoded() { + // skip cloning for testing key. + return encodedPriv; + } + }; + byte[] encodedPub = Base64.getDecoder().decode(PKCS1_PUB_STR); + CUSTOM_PUB = new PublicKey() { + @Override + public String getAlgorithm() { + return "RSA"; + } + @Override + public String getFormat() { + return "PKCS#1"; + } + @Override + public byte[] getEncoded() { + // skip cloning for testing key. + return encodedPub; + } + }; + } + static KeyStore getKeyStore() throws Exception { KeyStore ks; try (InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"))) { @@ -68,44 +146,64 @@ private static void testKey(Key key1, Key key2) throws Exception { throw new Exception("Format not PKCS#8"); } } - if (key1.equals(key2) == false) { - throw new Exception("Keys not equal"); + // skip equals check when key1 is custom key + if (key1 != CUSTOM_PRIV && key1 != CUSTOM_PUB) { + if (!key1.equals(key2)) { + throw new Exception("Keys not equal"); + } } - if (Arrays.equals(key1.getEncoded(), key2.getEncoded()) == false) { + // only compare encodings if keys are of the same format + if (key1.getFormat().equals(key2.getFormat()) && + !Arrays.equals(key1.getEncoded(), key2.getEncoded())) { throw new Exception("Encodings not equal"); } } - private static void testPublic(KeyFactory kf, PublicKey key) throws Exception { - System.out.println("Testing public key..."); + private static void testPublic(KeyFactory kf, PublicKey key) + throws Exception { + System.out.println("Testing " + (key == CUSTOM_PUB? "PKCS#1" : "") + + " public key..."); PublicKey key2 = (PublicKey)kf.translateKey(key); KeySpec rsaSpec = kf.getKeySpec(key, RSAPublicKeySpec.class); PublicKey key3 = kf.generatePublic(rsaSpec); KeySpec x509Spec = kf.getKeySpec(key, X509EncodedKeySpec.class); PublicKey key4 = kf.generatePublic(x509Spec); - KeySpec x509Spec2 = new X509EncodedKeySpec(key.getEncoded()); - PublicKey key5 = kf.generatePublic(x509Spec2); - testKey(key, key); + if (key != CUSTOM_PUB) { + testKey(key, key); + } testKey(key, key2); testKey(key, key3); testKey(key, key4); - testKey(key, key5); + + if (key.getFormat().equalsIgnoreCase("X.509")) { + KeySpec x509Spec2 = new X509EncodedKeySpec(key.getEncoded()); + PublicKey key5 = kf.generatePublic(x509Spec2); + testKey(key, key5); + } + } - private static void testPrivate(KeyFactory kf, PrivateKey key) throws Exception { - System.out.println("Testing private key..."); + private static void testPrivate(KeyFactory kf, PrivateKey key) + throws Exception { + System.out.println("Testing " + (key == CUSTOM_PRIV? "PKCS#1" : "") + + " private key..."); PrivateKey key2 = (PrivateKey)kf.translateKey(key); KeySpec rsaSpec = kf.getKeySpec(key, RSAPrivateCrtKeySpec.class); PrivateKey key3 = kf.generatePrivate(rsaSpec); KeySpec pkcs8Spec = kf.getKeySpec(key, PKCS8EncodedKeySpec.class); PrivateKey key4 = kf.generatePrivate(pkcs8Spec); - KeySpec pkcs8Spec2 = new PKCS8EncodedKeySpec(key.getEncoded()); - PrivateKey key5 = kf.generatePrivate(pkcs8Spec2); - testKey(key, key); + if (key != CUSTOM_PRIV) { + testKey(key, key); + } testKey(key, key2); testKey(key, key3); testKey(key, key4); - testKey(key, key5); + + if (key.getFormat().equalsIgnoreCase("PKCS#8")) { + KeySpec pkcs8Spec2 = new PKCS8EncodedKeySpec(key.getEncoded()); + PrivateKey key5 = kf.generatePrivate(pkcs8Spec2); + testKey(key, key5); + } // XXX PKCS#11 providers may not support non-CRT keys (e.g. NSS) // KeySpec rsaSpec2 = kf.getKeySpec(key, RSAPrivateKeySpec.class); @@ -145,6 +243,10 @@ public void main(Provider p) throws Exception { test(kf, ks.getCertificate(alias).getPublicKey()); } } + // repeat the test w/ PKCS#1 RSA Private Key + test(kf, CUSTOM_PRIV); + test(kf, CUSTOM_PUB); + long stop = System.currentTimeMillis(); System.out.println("All tests passed (" + (stop - start) + " ms)."); } diff --git a/test/jdk/sun/security/rsa/TestKeyFactory.java b/test/jdk/sun/security/rsa/TestKeyFactory.java index 548d1bf4a6f..4ab13e81767 100644 --- a/test/jdk/sun/security/rsa/TestKeyFactory.java +++ b/test/jdk/sun/security/rsa/TestKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /** * @test - * @bug 4853305 + * @bug 4853305 8023980 * @summary Test KeyFactory of the new RSA provider * @author Andreas Sterbenz */ @@ -41,6 +41,84 @@ public class TestKeyFactory { private static final char[] password = "test12".toCharArray(); + private static final String PKCS1_PRIV_STR = + // the BASE64 string between -----BEGIN RSA PRIVATE KEY----- + // and -----END RSA PRIVATE KEY----- + "MIIEowIBAAKCAQEA0OIArlYES4X1XMTLDordtN/XIWFE1wvhl40RsHWM2n99+Stp" + + "CCJCcUb5FJ2/kefj/XRwB6p5IMpIZrHZqC8XXzlX5fpiFaSu2xnk17oWUKoErW27" + + "Stm098pU2RoUxWPKVl+42a8iVp8tijNElBNFALCGi0zXOhcTxMh0q1Wk0UhMJqam" + + "v5YnCKmT4THwwGYn/KeK3M7Qa+o5MoVBHLbeT9LJgEmSluVzIh44Lh6weX0bw72P" + + "8X2praOhbzg2B343MqS/rMLw6On+0i7ccEgp23vX9G5w85q4A5FSIrk4S/pyv5sO" + + "rwjCQKBW1TS0/2iB9zNkFMj5/+h7l2oqTT7sSQIDAQABAoIBADn6sXOynoiUC1IP" + + "sck8lGOTSjSSujfyrVCSsJlJV6qCfuX9va6rS8QDjjnBu531PtxoSHxoPizy2Pvg" + + "W+kKATPGR/am9DjLuFlKq7GRjoYfWyMEdVtGaKvq9ng4fBF6LHyjHz0VFrPyhQJ6" + + "TovHeXzCguYBkzAlnbAeb/vqzs/kABbOuSHVi7DsaixCoEX9zOptFYQw/l8rh68+" + + "UF2bpNNH3jOC1uN3vZtuSwCupqtN+2Mpkx2h04Rk75vWIhrnPeMgmcd3yP4LNZMR" + + "mfaynb63RRzVkNis7+NVk016SQ1oL79mrBvy5rBg3HeCeArwvqZAmOaWsLSWHzCy" + + "zlVlMTECgYEA6JlnMpC956Qi8HX5ye4Hu2ovBdbNGtH/TMkZmColJz9P7CvNkNIb" + + "Od6mvLMydbPHkhdBUDWD4rhiCKHrf5zKju1i24YqWcvuSGotWj4/KQ3+87mLZM+7" + + "daBsJBmSEVB80sgA9ItqSgOyNoNFpiDgFnlszAfb0n9XXEzB/pwSw1UCgYEA5eXI" + + "d+eKugugP+n6CluQfyxfN6WWCzfqWToCTTxPn2i12AiEssXy+kyLjupJVLWSivdo" + + "83wD5LuxFRGc9P+aKQERPhb0AFaxf1llUCXla65/x2So5xjMvtuzgQ0OktPJqJXq" + + "hYGunctsr5rje33+7vlx4xWkrL2PrQWzJabn7SUCgYEAqw3FesY/Ik7u8u+P1xSZ" + + "0xXvptek1oiAu7NYgzLbR9WjrQc5kbsyEojPDg6qmSyxI5q+iYIRj3YRgk+xpJNl" + + "0154SQCNvKPghJiw6aDFSifkytA01tp9/a8QWCwF433RjiFPsoekjvHQ6Y34dofO" + + "xDhf7lwJKPBFCrfYIqocklECgYAIPI9OHHGP8NKw94UJ0fX/WGug5sHVbQ9sWvOy" + + "KLMBlxLMxqFadlUaOpvVZvdxnX++ktajwpGxJDhX9OWWsYGobm1buB7N1E1Prrg+" + + "gt0RWpMhZa3Xeb/8Jorr2Lfo8sWK0LQyTE8hQCSIthfoWL9FeJJn/GKF/dSj8kxU" + + "0QIGMQKBgG/8U/zZ87DzfXS81P1p+CmH474wmou4KD2/zXp/lDR9+dlIUeijlIbU" + + "P6Y5xJvT33Y40giW9irShgDHjZgw0ap11K3b2HzLImdPEaBiENo735rpLs8WLK9H" + + "+yeRbiP2y9To7sTihm9Jrkctzp6sqFtKyye1+S21X1tMz8NGfXen"; + + private static final String PKCS1_PUB_STR = + // the BASE64 string between -----BEGIN RSA PUBLIC KEY----- + // and -----END RSA PUBLIC KEY----- + "MIIBCgKCAQEA0OIArlYES4X1XMTLDordtN/XIWFE1wvhl40RsHWM2n99+StpCCJC" + + "cUb5FJ2/kefj/XRwB6p5IMpIZrHZqC8XXzlX5fpiFaSu2xnk17oWUKoErW27Stm0" + + "98pU2RoUxWPKVl+42a8iVp8tijNElBNFALCGi0zXOhcTxMh0q1Wk0UhMJqamv5Yn" + + "CKmT4THwwGYn/KeK3M7Qa+o5MoVBHLbeT9LJgEmSluVzIh44Lh6weX0bw72P8X2p" + + "raOhbzg2B343MqS/rMLw6On+0i7ccEgp23vX9G5w85q4A5FSIrk4S/pyv5sOrwjC" + + "QKBW1TS0/2iB9zNkFMj5/+h7l2oqTT7sSQIDAQAB"; + + + private static final PrivateKey P1_PRIV; + private static final PublicKey P1_PUB; + + static { + byte[] encodedPriv = Base64.getDecoder().decode(PKCS1_PRIV_STR); + P1_PRIV = new PrivateKey() { + @Override + public String getAlgorithm() { + return "RSA"; + } + @Override + public String getFormat() { + return "PKCS#1"; + } + @Override + public byte[] getEncoded() { + // skip cloning for testing key. + return encodedPriv; + } + }; + byte[] encodedPub = Base64.getDecoder().decode(PKCS1_PUB_STR); + P1_PUB = new PublicKey() { + @Override + public String getAlgorithm() { + return "RSA"; + } + @Override + public String getFormat() { + return "PKCS#1"; + } + @Override + public byte[] getEncoded() { + // skip cloning for testing key. + return encodedPub; + } + }; + } + static KeyStore getKeyStore() throws Exception { InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks")); KeyStore ks = KeyStore.getInstance("JKS"); @@ -63,55 +141,78 @@ private static void testKey(Key key1, Key key2) throws Exception { } } else if (key1 instanceof PrivateKey) { if (key2.getFormat().equals("PKCS#8") == false) { - throw new Exception("Format not PKCS#8"); + throw new Exception("Format not PKCS#8: " + key2.getFormat()); } } - if (key1.equals(key2) == false) { - throw new Exception("Keys not equal"); + // skip equals check when key1 is custom key + if (key1 != P1_PRIV && key1 != P1_PUB) { + if (!key1.equals(key2)) { + throw new Exception("Keys not equal"); + } } - if (Arrays.equals(key1.getEncoded(), key2.getEncoded()) == false) { + + // only compare encodings if keys are of the same format + if (key1.getFormat().equals(key2.getFormat()) && + !Arrays.equals(key1.getEncoded(), key2.getEncoded())) { throw new Exception("Encodings not equal"); } } - private static void testPublic(KeyFactory kf, PublicKey key) throws Exception { - System.out.println("Testing public key..."); + private static void testPublic(KeyFactory kf, PublicKey key) + throws Exception { + System.out.println("Testing " + (key == P1_PUB? "PKCS#1" : "") + + " public key..."); + PublicKey key2 = (PublicKey)kf.translateKey(key); KeySpec rsaSpec = kf.getKeySpec(key, RSAPublicKeySpec.class); PublicKey key3 = kf.generatePublic(rsaSpec); KeySpec x509Spec = kf.getKeySpec(key, X509EncodedKeySpec.class); PublicKey key4 = kf.generatePublic(x509Spec); - KeySpec x509Spec2 = new X509EncodedKeySpec(key.getEncoded()); - PublicKey key5 = kf.generatePublic(x509Spec2); - testKey(key, key); + if (key != P1_PUB) { + testKey(key, key); + } testKey(key, key2); testKey(key, key3); testKey(key, key4); - testKey(key, key5); + + if (key.getFormat().equalsIgnoreCase("X.509")) { + KeySpec x509Spec2 = new X509EncodedKeySpec(key.getEncoded()); + PublicKey key5 = kf.generatePublic(x509Spec2); + testKey(key, key5); + } } - private static void testPrivate(KeyFactory kf, PrivateKey key) throws Exception { - System.out.println("Testing private key..."); + private static void testPrivate(KeyFactory kf, PrivateKey key) + throws Exception { + System.out.println("Testing " + (key == P1_PRIV? "PKCS#1" : "") + + " private key..."); PrivateKey key2 = (PrivateKey)kf.translateKey(key); KeySpec rsaSpec = kf.getKeySpec(key, RSAPrivateCrtKeySpec.class); PrivateKey key3 = kf.generatePrivate(rsaSpec); KeySpec pkcs8Spec = kf.getKeySpec(key, PKCS8EncodedKeySpec.class); PrivateKey key4 = kf.generatePrivate(pkcs8Spec); - KeySpec pkcs8Spec2 = new PKCS8EncodedKeySpec(key.getEncoded()); - PrivateKey key5 = kf.generatePrivate(pkcs8Spec2); - testKey(key, key); + if (key != P1_PRIV) { + testKey(key, key); + } testKey(key, key2); testKey(key, key3); testKey(key, key4); - testKey(key, key5); + if (key.getFormat().equalsIgnoreCase("PKCS#8")) { + KeySpec pkcs8Spec2 = new PKCS8EncodedKeySpec(key.getEncoded()); + PrivateKey key5 = kf.generatePrivate(pkcs8Spec2); + testKey(key, key5); + } KeySpec rsaSpec2 = kf.getKeySpec(key, RSAPrivateKeySpec.class); PrivateKey key6 = kf.generatePrivate(rsaSpec2); - RSAPrivateKey rsaKey = (RSAPrivateKey)key; - KeySpec rsaSpec3 = new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent()); - PrivateKey key7 = kf.generatePrivate(rsaSpec3); testKey(key6, key6); - testKey(key6, key7); + if (key instanceof RSAPrivateKey) { + KeySpec rsaSpec3 = + new RSAPrivateKeySpec(((RSAPrivateKey)key).getModulus(), + ((RSAPrivateKey)key).getPrivateExponent()); + PrivateKey key7 = kf.generatePrivate(rsaSpec3); + testKey(key6, key7); + } } private static void test(KeyFactory kf, Key key) throws Exception { @@ -137,6 +238,10 @@ public static void main(String[] args) throws Exception { test(kf, ks.getCertificate(alias).getPublicKey()); } } + // repeat the test w/ PKCS#1 RSA Private Key + test(kf, P1_PRIV); + test(kf, P1_PUB); + long stop = System.currentTimeMillis(); System.out.println("All tests passed (" + (stop - start) + " ms)."); }