--- old/src/java.base/share/classes/java/security/spec/PSSParameterSpec.java 2019-06-28 07:24:51.552599600 +0200 +++ new/src/java.base/share/classes/java/security/spec/PSSParameterSpec.java 2019-06-28 07:24:50.217686100 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2019, 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 @@ -26,7 +26,6 @@ package java.security.spec; import java.util.Objects; -import java.security.spec.MGF1ParameterSpec; /** * This class specifies a parameter spec for RSASSA-PSS signature scheme, @@ -218,4 +217,14 @@ public int getTrailerField() { return trailerField; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("MD: " + mdName + "\n") + .append("MGF: " + mgfSpec + "\n") + .append("SaltLength: " + saltLen + "\n") + .append("TrailerField: " + trailerField + "\n"); + return sb.toString(); + } } --- old/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java 2019-06-28 07:25:05.403411300 +0200 +++ new/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java 2019-06-28 07:25:03.245182300 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, 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,7 +32,6 @@ import java.security.cert.CertificateException; import java.security.*; -import java.security.spec.AlgorithmParameterSpec; import java.util.Base64; @@ -237,10 +236,14 @@ */ AlgorithmId algId = null; try { - algId = AlgorithmId.get(signature.getAlgorithm()); + AlgorithmParameters params = signature.getParameters(); + algId = params == null + ? AlgorithmId.get(signature.getAlgorithm()) + : AlgorithmId.get(params); } catch (NoSuchAlgorithmException nsae) { throw new SignatureException(nsae); } + algId.encode(scratch); // sig algorithm scratch.putBitString(sig); // sig --- old/src/java.base/share/classes/sun/security/rsa/PSSParameters.java 2019-06-28 07:25:19.468164800 +0200 +++ new/src/java.base/share/classes/sun/security/rsa/PSSParameters.java 2019-06-28 07:25:17.348434300 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, 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 @@ -52,13 +52,7 @@ public final class PSSParameters extends AlgorithmParametersSpi { - private String mdName; - private MGF1ParameterSpec mgfSpec; - private int saltLength; - private int trailerField; - - private static final ObjectIdentifier OID_MGF1 = - ObjectIdentifier.newInternal(new int[] {1,2,840,113549,1,1,8}); + private PSSParameterSpec spec; public PSSParameters() { } @@ -71,9 +65,9 @@ ("Inappropriate parameter specification"); } PSSParameterSpec spec = (PSSParameterSpec) paramSpec; - this.mdName = spec.getDigestAlgorithm(); + String mgfName = spec.getMGFAlgorithm(); - if (!mgfName.equalsIgnoreCase("MGF1")) { + if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1")) { throw new InvalidParameterSpecException("Unsupported mgf " + mgfName + "; MGF1 only"); } @@ -82,31 +76,30 @@ throw new InvalidParameterSpecException("Inappropriate mgf " + "parameters; non-null MGF1ParameterSpec only"); } - this.mgfSpec = (MGF1ParameterSpec) mgfSpec; - this.saltLength = spec.getSaltLength(); - this.trailerField = spec.getTrailerField(); + this.spec = spec; } @Override protected void engineInit(byte[] encoded) throws IOException { // first initialize with the DEFAULT values before // retrieving from the encoding bytes - this.mdName = DEFAULT.getDigestAlgorithm(); - this.mgfSpec = (MGF1ParameterSpec) DEFAULT.getMGFParameters(); - this.saltLength = DEFAULT.getSaltLength(); - this.trailerField = DEFAULT.getTrailerField(); + String mdName = DEFAULT.getDigestAlgorithm(); + MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) DEFAULT.getMGFParameters(); + int saltLength = DEFAULT.getSaltLength(); + int trailerField = DEFAULT.getTrailerField(); DerInputStream der = new DerInputStream(encoded); DerValue[] datum = der.getSequence(4); + for (DerValue d : datum) { if (d.isContextSpecific((byte) 0x00)) { // hash algid - this.mdName = AlgorithmId.parse + mdName = AlgorithmId.parse (d.data.getDerValue()).getName(); } else if (d.isContextSpecific((byte) 0x01)) { // mgf algid AlgorithmId val = AlgorithmId.parse(d.data.getDerValue()); - if (!val.getOID().equals(OID_MGF1)) { + if (!val.getOID().equals(AlgorithmId.mgf1_oid)) { throw new IOException("Only MGF1 mgf is supported"); } AlgorithmId params = AlgorithmId.parse( @@ -114,25 +107,25 @@ String mgfDigestName = params.getName(); switch (mgfDigestName) { case "SHA-1": - this.mgfSpec = MGF1ParameterSpec.SHA1; + mgfSpec = MGF1ParameterSpec.SHA1; break; case "SHA-224": - this.mgfSpec = MGF1ParameterSpec.SHA224; + mgfSpec = MGF1ParameterSpec.SHA224; break; case "SHA-256": - this.mgfSpec = MGF1ParameterSpec.SHA256; + mgfSpec = MGF1ParameterSpec.SHA256; break; case "SHA-384": - this.mgfSpec = MGF1ParameterSpec.SHA384; + mgfSpec = MGF1ParameterSpec.SHA384; break; case "SHA-512": - this.mgfSpec = MGF1ParameterSpec.SHA512; + mgfSpec = MGF1ParameterSpec.SHA512; break; case "SHA-512/224": - this.mgfSpec = MGF1ParameterSpec.SHA512_224; + mgfSpec = MGF1ParameterSpec.SHA512_224; break; case "SHA-512/256": - this.mgfSpec = MGF1ParameterSpec.SHA512_256; + mgfSpec = MGF1ParameterSpec.SHA512_256; break; default: throw new IOException @@ -141,21 +134,24 @@ } } else if (d.isContextSpecific((byte) 0x02)) { // salt length - this.saltLength = d.data.getDerValue().getInteger(); - if (this.saltLength < 0) { + saltLength = d.data.getDerValue().getInteger(); + if (saltLength < 0) { throw new IOException("Negative value for saltLength"); } } else if (d.isContextSpecific((byte) 0x03)) { // trailer field - this.trailerField = d.data.getDerValue().getInteger(); - if (this.trailerField != 1) { + trailerField = d.data.getDerValue().getInteger(); + if (trailerField != 1) { throw new IOException("Unsupported trailerField value " + - this.trailerField); + trailerField); } } else { throw new IOException("Invalid encoded PSSParameters"); } } + + this.spec = new PSSParameterSpec(mdName, "MGF1", mgfSpec, + saltLength, trailerField); } @Override @@ -173,9 +169,7 @@ T engineGetParameterSpec(Class paramSpec) throws InvalidParameterSpecException { if (PSSParameterSpec.class.isAssignableFrom(paramSpec)) { - return paramSpec.cast( - new PSSParameterSpec(mdName, "MGF1", mgfSpec, - saltLength, trailerField)); + return paramSpec.cast(spec); } else { throw new InvalidParameterSpecException ("Inappropriate parameter specification"); @@ -184,72 +178,97 @@ @Override protected byte[] engineGetEncoded() throws IOException { + return getEncoded(spec); + } + + @Override + protected byte[] engineGetEncoded(String encMethod) throws IOException { + if ((encMethod != null) && + (!encMethod.equalsIgnoreCase("ASN.1"))) { + throw new IllegalArgumentException("Only support ASN.1 format"); + } + return engineGetEncoded(); + } + + @Override + protected String engineToString() { + return spec.toString(); + } + + /** + * Returns the encoding of a {@link PSSParameterSpec} object. This method + * is used in this class and {@link AlgorithmId}. + * + * @param spec a {@code PSSParameterSpec} object + * @return its DER encoding + * @throws IOException if the name of a MessageDigest or MaskGenAlgorithm + * is unsupported + */ + public static byte[] getEncoded(PSSParameterSpec spec) throws IOException { + + AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); + if (!(mgfSpec instanceof MGF1ParameterSpec)) { + throw new IOException("Cannot encode " + mgfSpec); + } + + MGF1ParameterSpec mgf1Spec = (MGF1ParameterSpec)mgfSpec; + DerOutputStream tmp = new DerOutputStream(); DerOutputStream tmp2, tmp3; // MD AlgorithmId mdAlgId; try { - mdAlgId = AlgorithmId.get(mdName); + mdAlgId = AlgorithmId.get(spec.getDigestAlgorithm()); } catch (NoSuchAlgorithmException nsae) { - throw new IOException("AlgorithmId " + mdName + - " impl not found"); + throw new IOException("AlgorithmId " + spec.getDigestAlgorithm() + + " impl not found"); + } + if (!mdAlgId.getOID().equals(AlgorithmId.SHA_oid)) { + tmp2 = new DerOutputStream(); + mdAlgId.derEncode(tmp2); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), + tmp2); } - tmp2 = new DerOutputStream(); - mdAlgId.derEncode(tmp2); - tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0), - tmp2); // MGF - tmp2 = new DerOutputStream(); - tmp2.putOID(OID_MGF1); AlgorithmId mgfDigestId; try { - mgfDigestId = AlgorithmId.get(mgfSpec.getDigestAlgorithm()); + mgfDigestId = AlgorithmId.get(mgf1Spec.getDigestAlgorithm()); } catch (NoSuchAlgorithmException nase) { throw new IOException("AlgorithmId " + - mgfSpec.getDigestAlgorithm() + " impl not found"); + mgf1Spec.getDigestAlgorithm() + " impl not found"); + } + + if (!mgfDigestId.getOID().equals(AlgorithmId.SHA_oid)) { + tmp2 = new DerOutputStream(); + tmp2.putOID(AlgorithmId.mgf1_oid); + mgfDigestId.encode(tmp2); + tmp3 = new DerOutputStream(); + tmp3.write(DerValue.tag_Sequence, tmp2); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), + tmp3); } - mgfDigestId.encode(tmp2); - tmp3 = new DerOutputStream(); - tmp3.write(DerValue.tag_Sequence, tmp2); - tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1), - tmp3); // SaltLength - tmp2 = new DerOutputStream(); - tmp2.putInteger(saltLength); - tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)2), - tmp2); + if (spec.getSaltLength() != 20) { + tmp2 = new DerOutputStream(); + tmp2.putInteger(spec.getSaltLength()); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 2), + tmp2); + } // TrailerField - tmp2 = new DerOutputStream(); - tmp2.putInteger(trailerField); - tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3), - tmp2); + if (spec.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) { + tmp2 = new DerOutputStream(); + tmp2.putInteger(spec.getTrailerField()); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 3), + tmp2); + } // Put all together under a SEQUENCE tag DerOutputStream out = new DerOutputStream(); out.write(DerValue.tag_Sequence, tmp); return out.toByteArray(); } - - @Override - protected byte[] engineGetEncoded(String encMethod) throws IOException { - if ((encMethod != null) && - (!encMethod.equalsIgnoreCase("ASN.1"))) { - throw new IllegalArgumentException("Only support ASN.1 format"); - } - return engineGetEncoded(); - } - - @Override - protected String engineToString() { - StringBuilder sb = new StringBuilder(); - sb.append("MD: " + mdName + "\n") - .append("MGF: MGF1" + mgfSpec.getDigestAlgorithm() + "\n") - .append("SaltLength: " + saltLength + "\n") - .append("TrailerField: " + trailerField + "\n"); - return sb.toString(); - } } --- old/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java 2019-06-28 07:25:33.148280100 +0200 +++ new/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java 2019-06-28 07:25:31.027587200 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, 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 @@ -30,12 +30,12 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateEncodingException; import java.security.*; +import java.security.spec.AlgorithmParameterSpec; import java.util.Date; import sun.security.pkcs10.PKCS10; import sun.security.x509.*; - /** * Generate a pair of keys, and provide access to them. This class is * provided primarily for ease of use. @@ -123,6 +123,21 @@ // want "public void generate (X509Certificate)" ... inherit DSA/D-H param + public void generate(int keyBits) { + if (keyBits != -1) { + try { + if (prng == null) { + prng = new SecureRandom(); + } + keyGen.initialize(keyBits, prng); + + } catch (Exception e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + generateInternal(); + } + /** * Generates a random public/private key pair, with a given key * size. Different algorithms provide different degrees of security @@ -140,21 +155,8 @@ * @exception InvalidKeyException if the environment does not * provide X.509 public keys for this signature algorithm. */ - public void generate (int keyBits) - throws InvalidKeyException - { - KeyPair pair; - - try { - if (prng == null) { - prng = new SecureRandom(); - } - keyGen.initialize(keyBits, prng); - pair = keyGen.generateKeyPair(); - - } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage()); - } + public void generateInternal() { + KeyPair pair = keyGen.generateKeyPair(); publicKey = pair.getPublic(); privateKey = pair.getPrivate(); @@ -262,12 +264,14 @@ new CertificateValidity(firstDate,lastDate); X509CertInfo info = new X509CertInfo(); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlg, privateKey); // Add all mandatory attributes info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( new java.util.Random().nextInt() & 0x7fffffff)); - AlgorithmId algID = AlgorithmId.get(sigAlg); + AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlg, params); info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algID)); info.set(X509CertInfo.SUBJECT, myname); @@ -277,13 +281,19 @@ if (ext != null) info.set(X509CertInfo.EXTENSIONS, ext); cert = new X509CertImpl(info); - cert.sign(privateKey, this.sigAlg); + cert.sign(privateKey, + params, + sigAlg, + null); return (X509Certificate)cert; } catch (IOException e) { throw new CertificateEncodingException("getSelfCert: " + e.getMessage()); + } catch (InvalidAlgorithmParameterException e2) { + throw new SignatureException( + "Unsupported PSSParameterSpec: " + e2.getMessage()); } } @@ -309,6 +319,7 @@ * @exception InvalidKeyException on key handling errors. * @exception SignatureException on signature handling errors. */ + // This method is not used inside JDK. Will not update it. public PKCS10 getCertRequest (X500Name myname) throws InvalidKeyException, SignatureException { --- old/src/java.base/share/classes/sun/security/tools/keytool/Main.java 2019-06-28 07:25:47.164406600 +0200 +++ new/src/java.base/share/classes/sun/security/tools/keytool/Main.java 2019-06-28 07:25:45.225482200 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -49,7 +49,7 @@ import java.security.cert.CertificateException; import java.security.cert.URICertStoreParameters; - +import java.security.spec.AlgorithmParameterSpec; import java.text.Collator; import java.text.MessageFormat; import java.util.*; @@ -1409,14 +1409,16 @@ signature.initSign(privateKey); X509CertInfo info = new X509CertInfo(); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlgName, privateKey); + AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params); info.set(X509CertInfo.VALIDITY, interval); info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( new java.util.Random().nextInt() & 0x7fffffff)); info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); info.set(X509CertInfo.ALGORITHM_ID, - new CertificateAlgorithmId( - AlgorithmId.get(sigAlgName))); + new CertificateAlgorithmId(algID)); info.set(X509CertInfo.ISSUER, issuer); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); @@ -1460,7 +1462,7 @@ signerCert.getPublicKey()); info.set(X509CertInfo.EXTENSIONS, ext); X509CertImpl cert = new X509CertImpl(info); - cert.sign(privateKey, sigAlgName); + cert.sign(privateKey, params, sigAlgName, null); dumpCert(cert, out); for (Certificate ca: keyStore.getCertificateChain(alias)) { if (ca instanceof X509Certificate) { @@ -1563,6 +1565,12 @@ Signature signature = Signature.getInstance(sigAlgName); signature.initSign(privKey); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlgName, privKey); + if (params != null) { + signature.setParameter(params); + } + X500Name subject = dname == null? new X500Name(((X509Certificate)cert).getSubjectDN().toString()): new X500Name(dname); @@ -2866,7 +2874,9 @@ // other solution: We first sign the cert, then retrieve the // outer sigalg and use it to set the inner sigalg X509CertImpl newCert = new X509CertImpl(certInfo); - newCert.sign(privKey, sigAlgName); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlgName, privKey); + newCert.sign(privKey, params, sigAlgName, null); AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG); certInfo.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgid); @@ -2883,7 +2893,7 @@ certInfo.set(X509CertInfo.EXTENSIONS, ext); // Sign the new certificate newCert = new X509CertImpl(certInfo); - newCert.sign(privKey, sigAlgName); + newCert.sign(privKey, params, sigAlgName, null); // Store the new certificate as a single-element certificate chain keyStore.setKeyEntry(alias, privKey, --- old/src/java.base/share/classes/sun/security/x509/AlgorithmId.java 2019-06-28 07:26:01.208242800 +0200 +++ new/src/java.base/share/classes/sun/security/x509/AlgorithmId.java 2019-06-28 07:25:59.380886500 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, 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 @@ -26,9 +26,14 @@ package sun.security.x509; import java.io.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; import java.util.*; import java.security.*; +import sun.security.rsa.PSSParameters; import sun.security.util.*; @@ -190,7 +195,12 @@ } else { bytes.putNull(); }*/ - bytes.putNull(); + if (algid.equals(RSASSA_PSS_oid)) { + // RFC 4055 3.3: when an RSASSA-PSS key does not require + // parameter validation, field is absent. + } else { + bytes.putNull(); + } } else { bytes.putDerValue(params); } @@ -689,6 +699,8 @@ oid(1, 2, 840, 113549, 1, 1, 1); public static final ObjectIdentifier RSAES_OAEP_oid = oid(1, 2, 840, 113549, 1, 1, 7); + public static final ObjectIdentifier mgf1_oid = + oid(1, 2, 840, 113549, 1, 1, 8); public static final ObjectIdentifier RSASSA_PSS_oid = oid(1, 2, 840, 113549, 1, 1, 10); @@ -1054,6 +1066,81 @@ } } + // Most commonly used PSSParameterSpec and AlgorithmId + private static class PSSParamsHolder { + + final static PSSParameterSpec PSS_256_SPEC = new PSSParameterSpec( + "SHA-256", "MGF1", + new MGF1ParameterSpec("SHA-256"), + 32, PSSParameterSpec.TRAILER_FIELD_BC); + final static PSSParameterSpec PSS_384_SPEC = new PSSParameterSpec( + "SHA-384", "MGF1", + new MGF1ParameterSpec("SHA-384"), + 48, PSSParameterSpec.TRAILER_FIELD_BC); + final static PSSParameterSpec PSS_512_SPEC = new PSSParameterSpec( + "SHA-512", "MGF1", + new MGF1ParameterSpec("SHA-512"), + 64, PSSParameterSpec.TRAILER_FIELD_BC); + + final static AlgorithmId PSS_256_ID; + final static AlgorithmId PSS_384_ID; + final static AlgorithmId PSS_512_ID; + + static { + try { + PSS_256_ID = new AlgorithmId(RSASSA_PSS_oid, + new DerValue(PSSParameters.getEncoded(PSS_256_SPEC))); + PSS_384_ID = new AlgorithmId(RSASSA_PSS_oid, + new DerValue(PSSParameters.getEncoded(PSS_384_SPEC))); + PSS_512_ID = new AlgorithmId(RSASSA_PSS_oid, + new DerValue(PSSParameters.getEncoded(PSS_512_SPEC))); + } catch (IOException e) { + throw new AssertionError("Should not happen", e); + } + } + } + + public static AlgorithmId getWithParameterSpec(String algName, + AlgorithmParameterSpec spec) throws NoSuchAlgorithmException { + + if (spec == null) { + return AlgorithmId.get(algName); + } else if (spec == PSSParamsHolder.PSS_256_SPEC) { + return PSSParamsHolder.PSS_256_ID; + } else if (spec == PSSParamsHolder.PSS_384_SPEC) { + return PSSParamsHolder.PSS_384_ID; + } else if (spec == PSSParamsHolder.PSS_512_SPEC) { + return PSSParamsHolder.PSS_512_ID; + } else { + try { + AlgorithmParameters result = + AlgorithmParameters.getInstance(algName); + result.init(spec); + return get(result); + } catch (InvalidParameterSpecException | NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + } + } + + public static PSSParameterSpec getDefaultAlgorithmParameterSpec( + String sigAlg, PrivateKey k) { + if (sigAlg.equalsIgnoreCase("RSASSA-PSS")) { + switch (ifcFfcStrength(KeyUtil.getKeySize(k))) { + case "SHA256": + return PSSParamsHolder.PSS_256_SPEC; + case "SHA384": + return PSSParamsHolder.PSS_384_SPEC; + case "SHA512": + return PSSParamsHolder.PSS_512_SPEC; + default: + throw new AssertionError("Should not happen"); + } + } else { + return null; + } + } + // Values from SP800-57 part 1 rev 4 tables 2 and 3 private static String ecStrength (int bitLength) { if (bitLength >= 512) { // 256 bits of strength --- old/src/java.base/share/classes/sun/security/x509/X509CertImpl.java 2019-06-28 07:26:14.930358400 +0200 +++ new/src/java.base/share/classes/sun/security/x509/X509CertImpl.java 2019-06-28 07:26:12.878258500 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, 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 @@ -42,7 +42,6 @@ import javax.security.auth.x500.X500Principal; -import java.util.Base64; import sun.security.util.*; import sun.security.provider.X509Factory; @@ -605,14 +604,10 @@ sigEngine.initSign(key); - // set parameters after Signature.initSign/initVerify call, so - // the deferred provider selection happens when the key is set - try { + if (signingParams != null) { + // set parameters after Signature.initSign/initVerify call, so + // the deferred provider selection happens when the key is set sigEngine.setParameter(signingParams); - } catch (UnsupportedOperationException e) { - // for backward compatibility, only re-throw when - // parameters is not null - if (signingParams != null) throw e; } // in case the name is reset --- /dev/null 2019-06-28 07:26:30.000000000 +0200 +++ new/test/jdk/sun/security/tools/keytool/PSS.java 2019-06-28 07:26:27.212065700 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +/* + * @test + * @bug 8215694 + * @summary keytool cannot generate RSASSA-PSS certificates + * @library /test/lib + * @modules java.base/sun.security.util + * java.base/sun.security.x509 + * @run main PSS + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.SecurityTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.security.DerUtils; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; + +import java.io.File; +import java.security.KeyStore; +import java.security.cert.X509Certificate; + +public class PSS { + + public static void main(String[] args) throws Exception { + + genkeypair("p", "-keyalg RSASSA-PSS -sigalg RSASSA-PSS") + .shouldHaveExitValue(0); + + genkeypair("a", "-keyalg RSA -sigalg RSASSA-PSS -keysize 2048") + .shouldHaveExitValue(0); + + genkeypair("b", "-keyalg RSA -sigalg RSASSA-PSS -keysize 4096") + .shouldHaveExitValue(0); + + genkeypair("c", "-keyalg RSA -sigalg RSASSA-PSS -keysize 8192") + .shouldHaveExitValue(0); + + KeyStore ks = KeyStore.getInstance( + new File("ks"), "changeit".toCharArray()); + + check((X509Certificate)ks.getCertificate("p"), "RSASSA-PSS", + AlgorithmId.SHA256_oid); + + check((X509Certificate)ks.getCertificate("a"), "RSA", + AlgorithmId.SHA256_oid); + + check((X509Certificate)ks.getCertificate("b"), "RSA", + AlgorithmId.SHA384_oid); + + check((X509Certificate)ks.getCertificate("c"), "RSA", + AlgorithmId.SHA512_oid); + + // More commands + kt("-certreq -alias p -sigalg RSASSA-PSS -file p.req") + .shouldHaveExitValue(0); + + kt("-gencert -alias a -sigalg RSASSA-PSS -infile p.req -outfile p.cert") + .shouldHaveExitValue(0); + + kt("-importcert -alias p -file p.cert") + .shouldHaveExitValue(0); + + kt("-selfcert -alias p -sigalg RSASSA-PSS") + .shouldHaveExitValue(0); + } + + static OutputAnalyzer genkeypair(String alias, String options) + throws Exception { + return kt("-genkeypair -alias " + alias + + " -dname CN=" + alias + " " + options); + } + + static OutputAnalyzer kt(String cmd) + throws Exception { + return SecurityTools.keytool("-storepass changeit -keypass changeit " + + "-keystore ks " + cmd); + } + + static void check(X509Certificate cert, String expectedKeyAlg, + ObjectIdentifier expectedMdAlg) throws Exception { + Asserts.assertEQ(cert.getPublicKey().getAlgorithm(), expectedKeyAlg); + Asserts.assertEQ(cert.getSigAlgName(), "RSASSA-PSS"); + DerUtils.checkAlg(cert.getSigAlgParams(), "000", expectedMdAlg); + } +} --- /dev/null 2019-06-28 07:26:42.000000000 +0200 +++ new/test/lib/jdk/test/lib/security/DerUtils.java 2019-06-28 07:26:38.634586500 +0200 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018, 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. + * + * 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 jdk.test.lib.security; + +import jdk.test.lib.Asserts; +import sun.security.util.DerInputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; + +import java.io.IOException; + +public class DerUtils { + /** + * Returns a DerValue (deep) inside another DerValue. + *

+ * The location of the inner DerValue is expressed as a string, in which + * each character is a step from the outer DerValue into the inner one. + * If it's a number n, the n'th element (starting from 0) of a sequence + * is the next step. If it's 'c', the content of an OctetString parsed + * as a DerValue is the next step. Note that n cannot be bigger than 9. + *

+ * Attention: do not reuse the return value. DerValue is mutable and + * reading it advances a pointer inside. + *

+ * For example, here is a PKCS #12 file: + *

+     * 0000:0845  [] SEQUENCE
+     * 0004:0003  [0]     INTEGER 3
+     * 0007:07FE  [1]     SEQUENCE
+     * 000B:000B  [10]         OID 1.2.840.113549.1.7.1 (data)
+     * 0016:07EF  [11]         cont [0]
+     * 001A:07EB  [110]             OCTET STRING
+     * ...
+     * 
+ * and the content of OCTET string at offset 001A can be parsed as another + * DerValue which is: + *
+     * 0000:07E7  [] SEQUENCE
+     * 0004:0303  [0]     SEQUENCE
+     * 0008:000B  [00]         OID 1.2.840.113549.1.7.1 (data)
+     * ....
+     * 
+ * Then the OID is {@code innerDerValue(data, "110c00").getOID()}. + * + * @param data the outer DerValue. We choose byte[] instead of DerValue + * because DerValue is mutable and cannot be reused. + * @param location the location of the inner DerValue + * @return the inner DerValue, or null if no DerValue is at the location + * @throws IOException if an I/O error happens + */ + public static DerValue innerDerValue(byte[] data, String location) + throws IOException { + + DerValue v = new DerValue(data); + for (char step : location.toCharArray()) { + if (step == 'c') { + v = new DerValue(v.getOctetString()); + } else { + DerInputStream ins = v.getData(); + // skip n DerValue in the sequence + for (int i = 0; i < step - '0'; i++) { + ins.getDerValue(); + } + if (ins.available() > 0) { + v = ins.getDerValue(); + } else { + return null; + } + } + } + return v; + } + + /** + * Ensures that the inner DerValue is the expected ObjectIdentifier. + */ + public static void checkAlg(byte[] der, String location, + ObjectIdentifier expected) throws Exception { + Asserts.assertEQ(innerDerValue(der, location).getOID(), expected); + } + + /** + * Ensures that the inner DerValue is the expected integer. + */ + public static void checkInt(byte[] der, String location, int expected) + throws Exception { + Asserts.assertEQ(innerDerValue(der, location).getInteger(), expected); + } + + /** + * Ensures that there is no inner DerValue at the specified location. + */ + public static void shouldNotExist(byte[] der, String location) + throws Exception { + Asserts.assertTrue(innerDerValue(der, location) == null); + } +}