| /* | |
| * Copyright (c) 2003, 2025, 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 | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.security.rsa; | |
| import java.io.IOException; | |
| import java.io.InvalidObjectException; | |
| import java.io.ObjectInputStream; | |
| import java.math.BigInteger; | |
| import java.security.*; | |
| import java.security.spec.*; | |
| import java.security.interfaces.*; | |
| import java.util.Arrays; | |
| import sun.security.util.*; | |
| import sun.security.pkcs.PKCS8Key; | |
| import sun.security.rsa.RSAUtil.KeyType; | |
| /** | |
| * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in CRT form. | |
| * For non-CRT private keys, see RSAPrivateKeyImpl. We need separate classes | |
| * to ensure correct behavior in instanceof checks, etc. | |
| * <p> | |
| * Note: RSA keys must be at least 512 bits long | |
| * | |
| * @see RSAPrivateKeyImpl | |
| * @see RSAKeyFactory | |
| * | |
| * @since 1.5 | |
| * @author Andreas Sterbenz | |
| */ | |
| public final class RSAPrivateCrtKeyImpl | |
| extends PKCS8Key implements RSAPrivateCrtKey { | |
| @java.io.Serial | |
| private static final long serialVersionUID = -1326088454257084918L; | |
| private BigInteger n; // modulus | |
| private BigInteger e; // public exponent | |
| private BigInteger d; // private exponent | |
| private BigInteger p; // prime p | |
| private BigInteger q; // prime q | |
| private BigInteger pe; // prime exponent p | |
| private BigInteger qe; // prime exponent q | |
| private BigInteger coeff; // CRT coefficient | |
| // RSA or RSA-PSS KeyType | |
| private final transient KeyType type; | |
| // Optional parameters associated with this RSA key | |
| // specified in the encoding of its AlgorithmId. | |
| // Must be null for "RSA" keys. | |
| private final transient AlgorithmParameterSpec keyParams; | |
| /** | |
| * 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(KeyType type, String format, | |
| byte[] encoded) throws InvalidKeyException { | |
| if (encoded == null || encoded.length == 0) { | |
| throw new InvalidKeyException("Missing key encoding"); | |
| } | |
| 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 (checkComponents(key)) { | |
| return key; | |
| } else { | |
| return new RSAPrivateKeyImpl(key.type, key.keyParams, | |
| key.getModulus(), key.getPrivateExponent()); | |
| } | |
| case "PKCS#1": | |
| try { | |
| BigInteger[] comps = parsePKCS1(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); | |
| } | |
| } | |
| /** | |
| * Validate if all CRT-specific components are available. | |
| */ | |
| static boolean checkComponents(RSAPrivateCrtKey key) { | |
| return !((key.getPublicExponent().signum() == 0) || | |
| (key.getPrimeExponentP().signum() == 0) || | |
| (key.getPrimeExponentQ().signum() == 0) || | |
| (key.getPrimeP().signum() == 0) || | |
| (key.getPrimeQ().signum() == 0) || | |
| (key.getCrtCoefficient().signum() == 0)); | |
| } | |
| /** | |
| * Generate a new key from the specified type and components. | |
| * Returns a CRT key if possible and a non-CRT key otherwise. | |
| * Used by SunPKCS11 provider. | |
| */ | |
| public static RSAPrivateKey newKey(KeyType type, | |
| AlgorithmParameterSpec params, | |
| BigInteger n, BigInteger e, BigInteger d, | |
| BigInteger p, BigInteger q, BigInteger pe, BigInteger qe, | |
| BigInteger coeff) throws InvalidKeyException { | |
| if ((e.signum() == 0) || (p.signum() == 0) || | |
| (q.signum() == 0) || (pe.signum() == 0) || | |
| (qe.signum() == 0) || (coeff.signum() == 0)) { | |
| // if any component is missing, return a non-CRT key | |
| return new RSAPrivateKeyImpl(type, params, n, d); | |
| } else { | |
| return new RSAPrivateCrtKeyImpl(type, params, n, e, d, | |
| p, q, pe, qe, coeff); | |
| } | |
| } | |
| /** | |
| * Construct a key from its encoding. Called from newKey above. | |
| */ | |
| private RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { | |
| super(encoded); | |
| parseKeyBits(); | |
| RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); | |
| try { | |
| // check the validity of oid and params | |
| Object[] o = RSAUtil.getTypeAndParamSpec(algid); | |
| this.type = (KeyType) o[0]; | |
| this.keyParams = (AlgorithmParameterSpec) o[1]; | |
| } catch (ProviderException e) { | |
| throw new InvalidKeyException(e); | |
| } | |
| } | |
| /** | |
| * Construct an RSA key from its components. Used by the | |
| * RSAKeyFactory and the RSAKeyPairGenerator. | |
| */ | |
| RSAPrivateCrtKeyImpl(KeyType type, AlgorithmParameterSpec keyParams, | |
| BigInteger n, BigInteger e, BigInteger d, | |
| BigInteger p, BigInteger q, BigInteger pe, BigInteger qe, | |
| BigInteger coeff) throws InvalidKeyException { | |
| RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); | |
| this.n = n; | |
| this.e = e; | |
| this.d = d; | |
| this.p = p; | |
| this.q = q; | |
| this.pe = pe; | |
| this.qe = qe; | |
| this.coeff = coeff; | |
| try { | |
| // validate and generate the algid encoding | |
| algid = RSAUtil.createAlgorithmId(type, keyParams); | |
| } catch (ProviderException exc) { | |
| throw new InvalidKeyException(exc); | |
| } | |
| this.type = type; | |
| this.keyParams = keyParams; | |
| byte[][] nbytes = new byte[8][]; | |
| nbytes[0] = n.toByteArray(); | |
| nbytes[1] = e.toByteArray(); | |
| nbytes[2] = d.toByteArray(); | |
| nbytes[3] = p.toByteArray(); | |
| nbytes[4] = q.toByteArray(); | |
| nbytes[5] = pe.toByteArray(); | |
| nbytes[6] = qe.toByteArray(); | |
| nbytes[7] = coeff.toByteArray(); | |
| // Initiate with a big enough size so there's no need to | |
| // reallocate memory later and thus can be cleaned up | |
| // reliably. | |
| DerOutputStream out = new DerOutputStream( | |
| nbytes[0].length + nbytes[1].length + | |
| nbytes[2].length + nbytes[3].length + | |
| nbytes[4].length + nbytes[5].length + | |
| nbytes[6].length + nbytes[7].length + | |
| 100); // Enough for version(3) and 8 tag+length(3 or 4) | |
| out.putInteger(0); // version must be 0 | |
| out.putInteger(nbytes[0]); | |
| out.putInteger(nbytes[1]); | |
| out.putInteger(nbytes[2]); | |
| out.putInteger(nbytes[3]); | |
| out.putInteger(nbytes[4]); | |
| out.putInteger(nbytes[5]); | |
| out.putInteger(nbytes[6]); | |
| out.putInteger(nbytes[7]); | |
| // Private values from [2] on. | |
| Arrays.fill(nbytes[2], (byte) 0); | |
| Arrays.fill(nbytes[3], (byte) 0); | |
| Arrays.fill(nbytes[4], (byte) 0); | |
| Arrays.fill(nbytes[5], (byte) 0); | |
| Arrays.fill(nbytes[6], (byte) 0); | |
| Arrays.fill(nbytes[7], (byte) 0); | |
| DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); | |
| privKeyMaterial = val.toByteArray(); | |
| val.clear(); | |
| } | |
| // see JCA doc | |
| @Override | |
| public String getAlgorithm() { | |
| return type.keyAlgo; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getModulus() { | |
| return n; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getPublicExponent() { | |
| return e; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getPrivateExponent() { | |
| return d; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getPrimeP() { | |
| return p; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getPrimeQ() { | |
| return q; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getPrimeExponentP() { | |
| return pe; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getPrimeExponentQ() { | |
| return qe; | |
| } | |
| // see JCA doc | |
| @Override | |
| public BigInteger getCrtCoefficient() { | |
| return coeff; | |
| } | |
| // see JCA doc | |
| @Override | |
| public AlgorithmParameterSpec getParams() { | |
| return keyParams; | |
| } | |
| // 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[] parsePKCS1(byte[] raw) throws IOException { | |
| DerValue derValue = new DerValue(raw); | |
| try { | |
| 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; | |
| } finally { | |
| derValue.clear(); | |
| } | |
| } | |
| private void parseKeyBits() throws InvalidKeyException { | |
| try { | |
| BigInteger[] comps = parsePKCS1(privKeyMaterial); | |
| 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); | |
| } | |
| } | |
| /** | |
| * With a given PKCS#1/slleay/OpenSSL old default RSA binary encoding, | |
| * decode and return the proper RSA encoded KeySpec | |
| * @param encoded RSA binary encoding | |
| * @return KeySpec | |
| * @throws InvalidKeyException on decoding failure | |
| */ | |
| public static KeySpec getKeySpec(byte[] encoded) throws | |
| InvalidKeyException { | |
| try { | |
| BigInteger[] comps = parsePKCS1(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 RSAPrivateKeySpec(comps[0], comps[2]); | |
| } else { | |
| return new RSAPrivateCrtKeySpec(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); | |
| } | |
| } | |
| /** | |
| * Restores the state of this object from the stream. | |
| * <p> | |
| * Deserialization of this object is not supported. | |
| * | |
| * @param stream the {@code ObjectInputStream} from which data is read | |
| * @throws IOException if an I/O error occurs | |
| * @throws ClassNotFoundException if a serialized class cannot be loaded | |
| */ | |
| @java.io.Serial | |
| private void readObject(ObjectInputStream stream) | |
| throws IOException, ClassNotFoundException { | |
| throw new InvalidObjectException( | |
| "RSAPrivateCrtKeyImpl keys are not directly deserializable"); | |
| } | |
| } |