28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30
31 import java.security.*;
32 import java.security.spec.AlgorithmParameterSpec;
33 import java.security.spec.PSSParameterSpec;
34 import java.security.spec.MGF1ParameterSpec;
35 import java.security.interfaces.*;
36
37 import java.util.Arrays;
38 import java.util.Hashtable;
39
40 import sun.security.util.*;
41 import sun.security.jca.JCAUtil;
42
43
44 /**
45 * PKCS#1 v2.2 RSASSA-PSS signatures with various message digest algorithms.
46 * RSASSA-PSS implementation takes the message digest algorithm, MGF algorithm,
47 * and salt length values through the required signature PSS parameters.
48 * We support SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, and
49 * SHA-512/256 message digest algorithms and MGF1 mask generation function.
50 *
51 * @since 11
52 */
53 public class RSAPSSSignature extends SignatureSpi {
54
55 private static final boolean DEBUG = false;
56
57 // utility method for comparing digest algorithms
58 // NOTE that first argument is assumed to be standard digest name
59 private boolean isDigestEqual(String stdAlg, String givenAlg) {
60 if (stdAlg == null || givenAlg == null) return false;
61
62 if (givenAlg.indexOf("-") != -1) {
63 return stdAlg.equalsIgnoreCase(givenAlg);
64 } else {
65 if (stdAlg.equals("SHA-1")) {
66 return (givenAlg.equalsIgnoreCase("SHA")
67 || givenAlg.equalsIgnoreCase("SHA1"));
68 } else {
69 StringBuilder sb = new StringBuilder(givenAlg);
70 // case-insensitive check
71 if (givenAlg.regionMatches(true, 0, "SHA", 0, 3)) {
72 givenAlg = sb.insert(3, "-").toString();
73 return stdAlg.equalsIgnoreCase(givenAlg);
74 } else {
75 throw new ProviderException("Unsupported digest algorithm "
76 + givenAlg);
77 }
78 }
79 }
80 }
81
82 private static final byte[] EIGHT_BYTES_OF_ZEROS = new byte[8];
83
84 private static final Hashtable<String, Integer> DIGEST_LENGTHS =
85 new Hashtable<String, Integer>();
86 static {
87 DIGEST_LENGTHS.put("SHA-1", 20);
88 DIGEST_LENGTHS.put("SHA", 20);
89 DIGEST_LENGTHS.put("SHA1", 20);
90 DIGEST_LENGTHS.put("SHA-224", 28);
91 DIGEST_LENGTHS.put("SHA224", 28);
92 DIGEST_LENGTHS.put("SHA-256", 32);
93 DIGEST_LENGTHS.put("SHA256", 32);
94 DIGEST_LENGTHS.put("SHA-384", 48);
95 DIGEST_LENGTHS.put("SHA384", 48);
96 DIGEST_LENGTHS.put("SHA-512", 64);
97 DIGEST_LENGTHS.put("SHA512", 64);
98 DIGEST_LENGTHS.put("SHA-512/224", 28);
99 DIGEST_LENGTHS.put("SHA512/224", 28);
100 DIGEST_LENGTHS.put("SHA-512/256", 32);
101 DIGEST_LENGTHS.put("SHA512/256", 32);
102 }
103
104 // message digest implementation we use for hashing the data
105 private MessageDigest md;
106 // flag indicating whether the digest is reset
107 private boolean digestReset = true;
108
109 // private key, if initialized for signing
110 private RSAPrivateKey privKey = null;
111 // public key, if initialized for verifying
112 private RSAPublicKey pubKey = null;
113 // PSS parameters from signatures and keys respectively
114 private PSSParameterSpec sigParams = null; // required for PSS signatures
115
116 // PRNG used to generate salt bytes if none given
117 private SecureRandom random;
118
119 /**
120 * Construct a new RSAPSSSignatur with arbitrary digest algorithm
121 */
202 e.printStackTrace();
203 }
204 return false;
205 }
206 }
207
208 /**
209 * Validate the specified RSAKey and its associated parameters against
210 * internal signature parameters.
211 */
212 private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException {
213 try {
214 AlgorithmParameterSpec keyParams = rsaKey.getParams();
215 // validate key parameters
216 if (!isCompatible(rsaKey.getParams(), this.sigParams)) {
217 throw new InvalidKeyException
218 ("Key contains incompatible PSS parameter values");
219 }
220 // validate key length
221 if (this.sigParams != null) {
222 Integer hLen =
223 DIGEST_LENGTHS.get(this.sigParams.getDigestAlgorithm());
224 if (hLen == null) {
225 throw new ProviderException("Unsupported digest algo: " +
226 this.sigParams.getDigestAlgorithm());
227 }
228 checkKeyLength(rsaKey, hLen, this.sigParams.getSaltLength());
229 }
230 return rsaKey;
231 } catch (SignatureException e) {
232 throw new InvalidKeyException(e);
233 }
234 }
235
236 /**
237 * Validate the specified Signature PSS parameters.
238 */
239 private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p)
240 throws InvalidAlgorithmParameterException {
241 if (p == null) {
242 throw new InvalidAlgorithmParameterException
243 ("Parameters cannot be null");
244 }
245 if (!(p instanceof PSSParameterSpec)) {
246 throw new InvalidAlgorithmParameterException
247 ("parameters must be type PSSParameterSpec");
248 }
255 if (key != null) {
256 if (!isCompatible(key.getParams(), params)) {
257 throw new InvalidAlgorithmParameterException
258 ("Signature parameters does not match key parameters");
259 }
260 }
261 // now sanity check the parameter values
262 if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) {
263 throw new InvalidAlgorithmParameterException("Only supports MGF1");
264
265 }
266 if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) {
267 throw new InvalidAlgorithmParameterException
268 ("Only supports TrailerFieldBC(1)");
269
270 }
271 String digestAlgo = params.getDigestAlgorithm();
272 // check key length again
273 if (key != null) {
274 try {
275 int hLen = DIGEST_LENGTHS.get(digestAlgo);
276 checkKeyLength(key, hLen, params.getSaltLength());
277 } catch (SignatureException e) {
278 throw new InvalidAlgorithmParameterException(e);
279 }
280 }
281 return params;
282 }
283
284 /**
285 * Ensure the object is initialized with key and parameters and
286 * reset digest
287 */
288 private void ensureInit() throws SignatureException {
289 RSAKey key = (this.privKey == null? this.pubKey : this.privKey);
290 if (key == null) {
291 throw new SignatureException("Missing key");
292 }
293 if (this.sigParams == null) {
294 // Parameters are required for signature verification
295 throw new SignatureException
|
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30
31 import java.security.*;
32 import java.security.spec.AlgorithmParameterSpec;
33 import java.security.spec.PSSParameterSpec;
34 import java.security.spec.MGF1ParameterSpec;
35 import java.security.interfaces.*;
36
37 import java.util.Arrays;
38 import java.util.Hashtable;
39
40 import sun.security.util.*;
41 import sun.security.jca.JCAUtil;
42
43
44 /**
45 * PKCS#1 v2.2 RSASSA-PSS signatures with various message digest algorithms.
46 * RSASSA-PSS implementation takes the message digest algorithm, MGF algorithm,
47 * and salt length values through the required signature PSS parameters.
48 * We support SHA-1, SHA-2 family and SHA3 family of message digest algorithms,
49 * and MGF1 mask generation function.
50 *
51 * @since 11
52 */
53 public class RSAPSSSignature extends SignatureSpi {
54
55 private static final boolean DEBUG = false;
56
57 // utility method for comparing digest algorithms
58 // NOTE that first argument is assumed to be standard digest name
59 private boolean isDigestEqual(String stdAlg, String givenAlg) {
60 if (stdAlg == null || givenAlg == null) return false;
61
62 if (givenAlg.indexOf("-") != -1) {
63 return stdAlg.equalsIgnoreCase(givenAlg);
64 } else {
65 if (stdAlg.equals("SHA-1")) {
66 return (givenAlg.equalsIgnoreCase("SHA")
67 || givenAlg.equalsIgnoreCase("SHA1"));
68 } else {
69 StringBuilder sb = new StringBuilder(givenAlg);
70 // case-insensitive check
71 if (givenAlg.regionMatches(true, 0, "SHA", 0, 3)) {
72 givenAlg = sb.insert(3, "-").toString();
73 return stdAlg.equalsIgnoreCase(givenAlg);
74 } else {
75 throw new ProviderException("Unsupported digest algorithm "
76 + givenAlg);
77 }
78 }
79 }
80 }
81
82 private static final byte[] EIGHT_BYTES_OF_ZEROS = new byte[8];
83
84 private static final Hashtable<KnownOIDs, Integer> DIGEST_LENGTHS =
85 new Hashtable<KnownOIDs, Integer>();
86 static {
87 DIGEST_LENGTHS.put(KnownOIDs.SHA_1, 20);
88 DIGEST_LENGTHS.put(KnownOIDs.SHA_224, 28);
89 DIGEST_LENGTHS.put(KnownOIDs.SHA_256, 32);
90 DIGEST_LENGTHS.put(KnownOIDs.SHA_384, 48);
91 DIGEST_LENGTHS.put(KnownOIDs.SHA_512, 64);
92 DIGEST_LENGTHS.put(KnownOIDs.SHA_512$224, 28);
93 DIGEST_LENGTHS.put(KnownOIDs.SHA_512$256, 32);
94 DIGEST_LENGTHS.put(KnownOIDs.SHA3_224, 28);
95 DIGEST_LENGTHS.put(KnownOIDs.SHA3_256, 32);
96 DIGEST_LENGTHS.put(KnownOIDs.SHA3_384, 48);
97 DIGEST_LENGTHS.put(KnownOIDs.SHA3_512, 64);
98 }
99
100 // message digest implementation we use for hashing the data
101 private MessageDigest md;
102 // flag indicating whether the digest is reset
103 private boolean digestReset = true;
104
105 // private key, if initialized for signing
106 private RSAPrivateKey privKey = null;
107 // public key, if initialized for verifying
108 private RSAPublicKey pubKey = null;
109 // PSS parameters from signatures and keys respectively
110 private PSSParameterSpec sigParams = null; // required for PSS signatures
111
112 // PRNG used to generate salt bytes if none given
113 private SecureRandom random;
114
115 /**
116 * Construct a new RSAPSSSignatur with arbitrary digest algorithm
117 */
198 e.printStackTrace();
199 }
200 return false;
201 }
202 }
203
204 /**
205 * Validate the specified RSAKey and its associated parameters against
206 * internal signature parameters.
207 */
208 private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException {
209 try {
210 AlgorithmParameterSpec keyParams = rsaKey.getParams();
211 // validate key parameters
212 if (!isCompatible(rsaKey.getParams(), this.sigParams)) {
213 throw new InvalidKeyException
214 ("Key contains incompatible PSS parameter values");
215 }
216 // validate key length
217 if (this.sigParams != null) {
218 String digestAlgo = this.sigParams.getDigestAlgorithm();
219 KnownOIDs ko = KnownOIDs.findMatch(digestAlgo);
220 if (ko != null) {
221 Integer hLen = DIGEST_LENGTHS.get(ko);
222 if (hLen != null) {
223 checkKeyLength(rsaKey, hLen,
224 this.sigParams.getSaltLength());
225 } else {
226 throw new ProviderException
227 ("Unsupported digest algo: " + digestAlgo);
228 }
229 } else {
230 throw new ProviderException
231 ("Unrecognized digest algo: " + digestAlgo);
232 }
233 }
234 return rsaKey;
235 } catch (SignatureException e) {
236 throw new InvalidKeyException(e);
237 }
238 }
239
240 /**
241 * Validate the specified Signature PSS parameters.
242 */
243 private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p)
244 throws InvalidAlgorithmParameterException {
245 if (p == null) {
246 throw new InvalidAlgorithmParameterException
247 ("Parameters cannot be null");
248 }
249 if (!(p instanceof PSSParameterSpec)) {
250 throw new InvalidAlgorithmParameterException
251 ("parameters must be type PSSParameterSpec");
252 }
259 if (key != null) {
260 if (!isCompatible(key.getParams(), params)) {
261 throw new InvalidAlgorithmParameterException
262 ("Signature parameters does not match key parameters");
263 }
264 }
265 // now sanity check the parameter values
266 if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) {
267 throw new InvalidAlgorithmParameterException("Only supports MGF1");
268
269 }
270 if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) {
271 throw new InvalidAlgorithmParameterException
272 ("Only supports TrailerFieldBC(1)");
273
274 }
275 String digestAlgo = params.getDigestAlgorithm();
276 // check key length again
277 if (key != null) {
278 try {
279 int hLen = DIGEST_LENGTHS.get(KnownOIDs.findMatch(digestAlgo));
280 checkKeyLength(key, hLen, params.getSaltLength());
281 } catch (SignatureException e) {
282 throw new InvalidAlgorithmParameterException(e);
283 }
284 }
285 return params;
286 }
287
288 /**
289 * Ensure the object is initialized with key and parameters and
290 * reset digest
291 */
292 private void ensureInit() throws SignatureException {
293 RSAKey key = (this.privKey == null? this.pubKey : this.privKey);
294 if (key == null) {
295 throw new SignatureException("Missing key");
296 }
297 if (this.sigParams == null) {
298 // Parameters are required for signature verification
299 throw new SignatureException
|