1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.rsa; 27 28 import java.io.*; 29 import sun.security.util.*; 30 import sun.security.x509.*; 31 import java.security.AlgorithmParametersSpi; 32 import java.security.NoSuchAlgorithmException; 33 import java.security.spec.AlgorithmParameterSpec; 34 import java.security.spec.InvalidParameterSpecException; 35 import java.security.spec.MGF1ParameterSpec; 36 import java.security.spec.PSSParameterSpec; 37 import static java.security.spec.PSSParameterSpec.DEFAULT; 38 39 /** 40 * This class implements the PSS parameters used with the RSA 41 * signatures in PSS padding. Here is its ASN.1 definition: 42 * RSASSA-PSS-params ::= SEQUENCE { 43 * hashAlgorithm [0] HashAlgorithm DEFAULT sha1, 44 * maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, 45 * saltLength [2] INTEGER DEFAULT 20 46 * trailerField [3] TrailerField DEFAULT trailerFieldBC 47 * } 48 * 49 * @author Valerie Peng 50 * 51 */ 52 53 public final class PSSParameters extends AlgorithmParametersSpi { 54 55 private String mdName; 56 private MGF1ParameterSpec mgfSpec; 57 private int saltLength; 58 private int trailerField; 59 60 private static final ObjectIdentifier OID_MGF1 = 61 ObjectIdentifier.newInternal(new int[] {1,2,840,113549,1,1,8}); 62 63 public PSSParameters() { 64 } 65 66 @Override 67 protected void engineInit(AlgorithmParameterSpec paramSpec) 68 throws InvalidParameterSpecException { 69 if (!(paramSpec instanceof PSSParameterSpec)) { 70 throw new InvalidParameterSpecException 71 ("Inappropriate parameter specification"); 72 } 73 PSSParameterSpec spec = (PSSParameterSpec) paramSpec; 74 this.mdName = spec.getDigestAlgorithm(); 75 String mgfName = spec.getMGFAlgorithm(); 76 if (!mgfName.equalsIgnoreCase("MGF1")) { 77 throw new InvalidParameterSpecException("Unsupported mgf " + 78 mgfName + "; MGF1 only"); 79 } 80 AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); 81 if (!(mgfSpec instanceof MGF1ParameterSpec)) { 82 throw new InvalidParameterSpecException("Inappropriate mgf " + 83 "parameters; non-null MGF1ParameterSpec only"); 84 } 85 this.mgfSpec = (MGF1ParameterSpec) mgfSpec; 86 this.saltLength = spec.getSaltLength(); 87 this.trailerField = spec.getTrailerField(); 88 } 89 90 @Override 91 protected void engineInit(byte[] encoded) throws IOException { 92 // first initialize with the DEFAULT values before 93 // retrieving from the encoding bytes 94 this.mdName = DEFAULT.getDigestAlgorithm(); 95 this.mgfSpec = (MGF1ParameterSpec) DEFAULT.getMGFParameters(); 96 this.saltLength = DEFAULT.getSaltLength(); 97 this.trailerField = DEFAULT.getTrailerField(); 98 99 DerInputStream der = new DerInputStream(encoded); 100 DerValue[] datum = der.getSequence(4); 101 for (DerValue d : datum) { 102 if (d.isContextSpecific((byte) 0x00)) { 103 // hash algid 104 this.mdName = AlgorithmId.parse 105 (d.data.getDerValue()).getName(); 106 } else if (d.isContextSpecific((byte) 0x01)) { 107 // mgf algid 108 AlgorithmId val = AlgorithmId.parse(d.data.getDerValue()); 109 if (!val.getOID().equals(OID_MGF1)) { 110 throw new IOException("Only MGF1 mgf is supported"); 111 } 112 AlgorithmId params = AlgorithmId.parse( 113 new DerValue(val.getEncodedParams())); 114 String mgfDigestName = params.getName(); 115 switch (mgfDigestName) { 116 case "SHA-1": 117 this.mgfSpec = MGF1ParameterSpec.SHA1; 118 break; 119 case "SHA-224": 120 this.mgfSpec = MGF1ParameterSpec.SHA224; 121 break; 122 case "SHA-256": 123 this.mgfSpec = MGF1ParameterSpec.SHA256; 124 break; 125 case "SHA-384": 126 this.mgfSpec = MGF1ParameterSpec.SHA384; 127 break; 128 case "SHA-512": 129 this.mgfSpec = MGF1ParameterSpec.SHA512; 130 break; 131 case "SHA-512/224": 132 this.mgfSpec = MGF1ParameterSpec.SHA512_224; 133 break; 134 case "SHA-512/256": 135 this.mgfSpec = MGF1ParameterSpec.SHA512_256; 136 break; 137 default: 138 throw new IOException 139 ("Unrecognized message digest algorithm " + 140 mgfDigestName); 141 } 142 } else if (d.isContextSpecific((byte) 0x02)) { 143 // salt length 144 this.saltLength = d.data.getDerValue().getInteger(); 145 if (this.saltLength < 0) { 146 throw new IOException("Negative value for saltLength"); 147 } 148 } else if (d.isContextSpecific((byte) 0x03)) { 149 // trailer field 150 this.trailerField = d.data.getDerValue().getInteger(); 151 if (this.trailerField != 1) { 152 throw new IOException("Unsupported trailerField value " + 153 this.trailerField); 154 } 155 } else { 156 throw new IOException("Invalid encoded PSSParameters"); 157 } 158 } 159 } 160 161 @Override 162 protected void engineInit(byte[] encoded, String decodingMethod) 163 throws IOException { 164 if ((decodingMethod != null) && 165 (!decodingMethod.equalsIgnoreCase("ASN.1"))) { 166 throw new IllegalArgumentException("Only support ASN.1 format"); 167 } 168 engineInit(encoded); 169 } 170 171 @Override 172 protected <T extends AlgorithmParameterSpec> 173 T engineGetParameterSpec(Class<T> paramSpec) 174 throws InvalidParameterSpecException { 175 if (PSSParameterSpec.class.isAssignableFrom(paramSpec)) { 176 return paramSpec.cast( 177 new PSSParameterSpec(mdName, "MGF1", mgfSpec, 178 saltLength, trailerField)); 179 } else { 180 throw new InvalidParameterSpecException 181 ("Inappropriate parameter specification"); 182 } 183 } 184 185 @Override 186 protected byte[] engineGetEncoded() throws IOException { 187 DerOutputStream tmp = new DerOutputStream(); 188 DerOutputStream tmp2, tmp3; 189 190 // MD 191 AlgorithmId mdAlgId; 192 try { 193 mdAlgId = AlgorithmId.get(mdName); 194 } catch (NoSuchAlgorithmException nsae) { 195 throw new IOException("AlgorithmId " + mdName + 196 " impl not found"); 197 } 198 tmp2 = new DerOutputStream(); 199 mdAlgId.derEncode(tmp2); 200 tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0), 201 tmp2); 202 203 // MGF 204 tmp2 = new DerOutputStream(); 205 tmp2.putOID(OID_MGF1); 206 AlgorithmId mgfDigestId; 207 try { 208 mgfDigestId = AlgorithmId.get(mgfSpec.getDigestAlgorithm()); 209 } catch (NoSuchAlgorithmException nase) { 210 throw new IOException("AlgorithmId " + 211 mgfSpec.getDigestAlgorithm() + " impl not found"); 212 } 213 mgfDigestId.encode(tmp2); 214 tmp3 = new DerOutputStream(); 215 tmp3.write(DerValue.tag_Sequence, tmp2); 216 tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1), 217 tmp3); 218 219 // SaltLength 220 tmp2 = new DerOutputStream(); 221 tmp2.putInteger(saltLength); 222 tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)2), 223 tmp2); 224 225 // TrailerField 226 tmp2 = new DerOutputStream(); 227 tmp2.putInteger(trailerField); 228 tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3), 229 tmp2); 230 231 // Put all together under a SEQUENCE tag 232 DerOutputStream out = new DerOutputStream(); 233 out.write(DerValue.tag_Sequence, tmp); 234 return out.toByteArray(); 235 } 236 237 @Override 238 protected byte[] engineGetEncoded(String encMethod) throws IOException { 239 if ((encMethod != null) && 240 (!encMethod.equalsIgnoreCase("ASN.1"))) { 241 throw new IllegalArgumentException("Only support ASN.1 format"); 242 } 243 return engineGetEncoded(); 244 } 245 246 @Override 247 protected String engineToString() { 248 StringBuilder sb = new StringBuilder(); 249 sb.append("MD: " + mdName + "\n") 250 .append("MGF: MGF1" + mgfSpec.getDigestAlgorithm() + "\n") 251 .append("SaltLength: " + saltLength + "\n") 252 .append("TrailerField: " + trailerField + "\n"); 253 return sb.toString(); 254 } 255 }