001: /*
002: * Portions Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: /*
026: * Copyright 1999-2004 The Apache Software Foundation.
027: */
028: /*
029: * $Id: DOMSignatureMethod.java,v 1.20.4.1 2005/08/12 14:23:49 mullan Exp $
030: */
031: package org.jcp.xml.dsig.internal.dom;
032:
033: import javax.xml.crypto.*;
034: import javax.xml.crypto.dom.DOMCryptoContext;
035: import javax.xml.crypto.dsig.*;
036: import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec;
037:
038: import java.io.IOException;
039: import java.security.*;
040: import java.security.spec.AlgorithmParameterSpec;
041: import java.util.logging.Level;
042: import java.util.logging.Logger;
043: import org.w3c.dom.Document;
044: import org.w3c.dom.Element;
045: import org.w3c.dom.Node;
046:
047: import org.jcp.xml.dsig.internal.SignerOutputStream;
048:
049: /**
050: * DOM-based abstract implementation of SignatureMethod.
051: *
052: * @author Sean Mullan
053: */
054: public abstract class DOMSignatureMethod extends DOMStructure implements
055: SignatureMethod {
056:
057: private static Logger log = Logger
058: .getLogger("org.jcp.xml.dsig.internal.dom");
059:
060: // see RFC 4051 for these algorithm definitions
061: final static String RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
062: final static String RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384";
063: final static String RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512";
064: final static String HMAC_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
065: final static String HMAC_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha384";
066: final static String HMAC_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512";
067:
068: private SignatureMethodParameterSpec params;
069: private Signature signature;
070:
071: /**
072: * Creates a <code>DOMSignatureMethod</code>.
073: *
074: * @param params the algorithm-specific params (may be <code>null</code>)
075: * @throws InvalidAlgorithmParameterException if the parameters are not
076: * appropriate for this signature method
077: */
078: DOMSignatureMethod(AlgorithmParameterSpec params)
079: throws InvalidAlgorithmParameterException {
080: if (params != null
081: && !(params instanceof SignatureMethodParameterSpec)) {
082: throw new InvalidAlgorithmParameterException(
083: "params must be of type SignatureMethodParameterSpec");
084: }
085: checkParams((SignatureMethodParameterSpec) params);
086: this .params = (SignatureMethodParameterSpec) params;
087: }
088:
089: /**
090: * Creates a <code>DOMSignatureMethod</code> from an element. This ctor
091: * invokes the abstract {@link #unmarshalParams unmarshalParams} method to
092: * unmarshal any algorithm-specific input parameters.
093: *
094: * @param smElem a SignatureMethod element
095: */
096: DOMSignatureMethod(Element smElem) throws MarshalException {
097: Element paramsElem = DOMUtils.getFirstChildElement(smElem);
098: if (paramsElem != null) {
099: params = unmarshalParams(paramsElem);
100: }
101: try {
102: checkParams(params);
103: } catch (InvalidAlgorithmParameterException iape) {
104: throw new MarshalException(iape);
105: }
106: }
107:
108: static SignatureMethod unmarshal(Element smElem)
109: throws MarshalException {
110: String alg = DOMUtils.getAttributeValue(smElem, "Algorithm");
111: if (alg.equals(SignatureMethod.RSA_SHA1)) {
112: return new SHA1withRSA(smElem);
113: } else if (alg.equals(RSA_SHA256)) {
114: return new SHA256withRSA(smElem);
115: } else if (alg.equals(RSA_SHA384)) {
116: return new SHA384withRSA(smElem);
117: } else if (alg.equals(RSA_SHA512)) {
118: return new SHA512withRSA(smElem);
119: } else if (alg.equals(SignatureMethod.DSA_SHA1)) {
120: return new SHA1withDSA(smElem);
121: } else if (alg.equals(SignatureMethod.HMAC_SHA1)) {
122: return new DOMHMACSignatureMethod.SHA1(smElem);
123: } else if (alg.equals(HMAC_SHA256)) {
124: return new DOMHMACSignatureMethod.SHA256(smElem);
125: } else if (alg.equals(HMAC_SHA384)) {
126: return new DOMHMACSignatureMethod.SHA384(smElem);
127: } else if (alg.equals(HMAC_SHA512)) {
128: return new DOMHMACSignatureMethod.SHA512(smElem);
129: } else {
130: throw new MarshalException(
131: "unsupported SignatureMethod algorithm: " + alg);
132: }
133: }
134:
135: /**
136: * Checks if the specified parameters are valid for this algorithm. By
137: * default, this method throws an exception if parameters are specified
138: * since most SignatureMethod algorithms do not have parameters. Subclasses
139: * should override it if they have parameters.
140: *
141: * @param params the algorithm-specific params (may be <code>null</code>)
142: * @throws InvalidAlgorithmParameterException if the parameters are not
143: * appropriate for this signature method
144: */
145: void checkParams(SignatureMethodParameterSpec params)
146: throws InvalidAlgorithmParameterException {
147: if (params != null) {
148: throw new InvalidAlgorithmParameterException(
149: "no parameters " + "should be specified for the "
150: + getSignatureAlgorithm()
151: + " SignatureMethod algorithm");
152: }
153: }
154:
155: public final AlgorithmParameterSpec getParameterSpec() {
156: return params;
157: }
158:
159: /**
160: * Unmarshals <code>SignatureMethodParameterSpec</code> from the specified
161: * <code>Element</code>. By default, this method throws an exception since
162: * most SignatureMethod algorithms do not have parameters. Subclasses should
163: * override it if they have parameters.
164: *
165: * @param paramsElem the <code>Element</code> holding the input params
166: * @return the algorithm-specific <code>SignatureMethodParameterSpec</code>
167: * @throws MarshalException if the parameters cannot be unmarshalled
168: */
169: SignatureMethodParameterSpec unmarshalParams(Element paramsElem)
170: throws MarshalException {
171: throw new MarshalException("no parameters should "
172: + "be specified for the " + getSignatureAlgorithm()
173: + " SignatureMethod algorithm");
174: }
175:
176: /**
177: * This method invokes the abstract {@link #marshalParams marshalParams}
178: * method to marshal any algorithm-specific parameters.
179: */
180: public void marshal(Node parent, String dsPrefix,
181: DOMCryptoContext context) throws MarshalException {
182: Document ownerDoc = DOMUtils.getOwnerDocument(parent);
183:
184: Element smElem = DOMUtils.createElement(ownerDoc,
185: "SignatureMethod", XMLSignature.XMLNS, dsPrefix);
186: DOMUtils.setAttribute(smElem, "Algorithm", getAlgorithm());
187:
188: if (params != null) {
189: marshalParams(smElem, dsPrefix);
190: }
191:
192: parent.appendChild(smElem);
193: }
194:
195: /**
196: * Verifies the passed-in signature with the specified key, using the
197: * underlying signature or MAC algorithm.
198: *
199: * @param key the verification key
200: * @param si the DOMSignedInfo
201: * @param signature the signature bytes to be verified
202: * @param context the XMLValidateContext
203: * @return <code>true</code> if the signature verified successfully,
204: * <code>false</code> if not
205: * @throws NullPointerException if <code>key</code>, <code>si</code> or
206: * <code>signature</code> are <code>null</code>
207: * @throws InvalidKeyException if the key is improperly encoded, of
208: * the wrong type, or parameters are missing, etc
209: * @throws SignatureException if an unexpected error occurs, such
210: * as the passed in signature is improperly encoded
211: * @throws XMLSignatureException if an unexpected error occurs
212: */
213: boolean verify(Key key, DOMSignedInfo si, byte[] sig,
214: XMLValidateContext context) throws InvalidKeyException,
215: SignatureException, XMLSignatureException {
216: if (key == null || si == null || sig == null) {
217: throw new NullPointerException();
218: }
219:
220: if (!(key instanceof PublicKey)) {
221: throw new InvalidKeyException("key must be PublicKey");
222: }
223: if (signature == null) {
224: try {
225: signature = Signature
226: .getInstance(getSignatureAlgorithm());
227: } catch (NoSuchAlgorithmException nsae) {
228: throw new XMLSignatureException(nsae);
229: }
230: }
231: signature.initVerify((PublicKey) key);
232: if (log.isLoggable(Level.FINE)) {
233: log.log(Level.FINE, "Signature provider:"
234: + signature.getProvider());
235: log.log(Level.FINE, "verifying with key: " + key);
236: }
237: si.canonicalize(context, new SignerOutputStream(signature));
238:
239: if (getAlgorithm().equals(SignatureMethod.DSA_SHA1)) {
240: try {
241: return signature.verify(convertXMLDSIGtoASN1(sig));
242: } catch (IOException ioe) {
243: throw new XMLSignatureException(ioe);
244: }
245: } else {
246: return signature.verify(sig);
247: }
248: }
249:
250: /**
251: * Signs the bytes with the specified key, using the underlying
252: * signature or MAC algorithm.
253: *
254: * @param key the signing key
255: * @param si the DOMSignedInfo
256: * @param context the XMLSignContext
257: * @return the signature
258: * @throws NullPointerException if <code>key</code> or
259: * <code>si</code> are <code>null</code>
260: * @throws InvalidKeyException if the key is improperly encoded, of
261: * the wrong type, or parameters are missing, etc
262: * @throws XMLSignatureException if an unexpected error occurs
263: */
264: byte[] sign(Key key, DOMSignedInfo si, XMLSignContext context)
265: throws InvalidKeyException, XMLSignatureException {
266: if (key == null || si == null) {
267: throw new NullPointerException();
268: }
269:
270: if (!(key instanceof PrivateKey)) {
271: throw new InvalidKeyException("key must be PrivateKey");
272: }
273: if (signature == null) {
274: try {
275: signature = Signature
276: .getInstance(getSignatureAlgorithm());
277: } catch (NoSuchAlgorithmException nsae) {
278: throw new XMLSignatureException(nsae);
279: }
280: }
281: signature.initSign((PrivateKey) key);
282: if (log.isLoggable(Level.FINE)) {
283: log.log(Level.FINE, "Signature provider:"
284: + signature.getProvider());
285: log.log(Level.FINE, "Signing with key: " + key);
286: }
287:
288: si.canonicalize(context, new SignerOutputStream(signature));
289:
290: try {
291: if (getAlgorithm().equals(SignatureMethod.DSA_SHA1)) {
292: return convertASN1toXMLDSIG(signature.sign());
293: } else {
294: return signature.sign();
295: }
296: } catch (SignatureException se) {
297: throw new XMLSignatureException(se);
298: } catch (IOException ioe) {
299: throw new XMLSignatureException(ioe);
300: }
301: }
302:
303: /**
304: * Marshals the algorithm-specific parameters to an Element and
305: * appends it to the specified parent element. By default, this method
306: * throws an exception since most SignatureMethod algorithms do not have
307: * parameters. Subclasses should override it if they have parameters.
308: *
309: * @param parent the parent element to append the parameters to
310: * @param paramsPrefix the algorithm parameters prefix to use
311: * @throws MarshalException if the parameters cannot be marshalled
312: */
313: void marshalParams(Element parent, String paramsPrefix)
314: throws MarshalException {
315: throw new MarshalException("no parameters should "
316: + "be specified for the " + getSignatureAlgorithm()
317: + " SignatureMethod algorithm");
318: }
319:
320: /**
321: * Returns the java.security.Signature standard algorithm name.
322: */
323: abstract String getSignatureAlgorithm();
324:
325: /**
326: * Returns true if parameters are equal; false otherwise.
327: *
328: * Subclasses should override this method to compare algorithm-specific
329: * parameters.
330: */
331: boolean paramsEqual(AlgorithmParameterSpec spec) {
332: return (getParameterSpec() == spec);
333: }
334:
335: public boolean equals(Object o) {
336: if (this == o) {
337: return true;
338: }
339:
340: if (!(o instanceof SignatureMethod)) {
341: return false;
342: }
343: SignatureMethod osm = (SignatureMethod) o;
344:
345: return (getAlgorithm().equals(osm.getAlgorithm()) && paramsEqual(osm
346: .getParameterSpec()));
347: }
348:
349: /**
350: * Converts an ASN.1 DSA value to a XML Signature DSA Value.
351: *
352: * The JAVA JCE DSA Signature algorithm creates ASN.1 encoded (r,s) value
353: * pairs; the XML Signature requires the core BigInteger values.
354: *
355: * @param asn1Bytes
356: *
357: * @throws IOException
358: * @see <A HREF="http://www.w3.org/TR/xmldsig-core/#dsa-sha1">6.4.1 DSA</A>
359: */
360: private static byte[] convertASN1toXMLDSIG(byte asn1Bytes[])
361: throws IOException {
362:
363: // THIS CODE IS COPIED FROM APACHE (see copyright at top of file)
364: byte rLength = asn1Bytes[3];
365: int i;
366:
367: for (i = rLength; (i > 0)
368: && (asn1Bytes[(4 + rLength) - i] == 0); i--)
369: ;
370:
371: byte sLength = asn1Bytes[5 + rLength];
372: int j;
373:
374: for (j = sLength; (j > 0)
375: && (asn1Bytes[(6 + rLength + sLength) - j] == 0); j--)
376: ;
377:
378: if ((asn1Bytes[0] != 48)
379: || (asn1Bytes[1] != asn1Bytes.length - 2)
380: || (asn1Bytes[2] != 2) || (i > 20)
381: || (asn1Bytes[4 + rLength] != 2) || (j > 20)) {
382: throw new IOException(
383: "Invalid ASN.1 format of DSA signature");
384: } else {
385: byte xmldsigBytes[] = new byte[40];
386:
387: System.arraycopy(asn1Bytes, (4 + rLength) - i,
388: xmldsigBytes, 20 - i, i);
389: System.arraycopy(asn1Bytes, (6 + rLength + sLength) - j,
390: xmldsigBytes, 40 - j, j);
391:
392: return xmldsigBytes;
393: }
394: }
395:
396: /**
397: * Converts a XML Signature DSA Value to an ASN.1 DSA value.
398: *
399: * The JAVA JCE DSA Signature algorithm creates ASN.1 encoded (r,s) value
400: * pairs; the XML Signature requires the core BigInteger values.
401: *
402: * @param xmldsigBytes
403: *
404: * @throws IOException
405: * @see <A HREF="http://www.w3.org/TR/xmldsig-core/#dsa-sha1">6.4.1 DSA</A>
406: */
407: private static byte[] convertXMLDSIGtoASN1(byte xmldsigBytes[])
408: throws IOException {
409:
410: // THIS CODE IS COPIED FROM APACHE (see copyright at top of file)
411: if (xmldsigBytes.length != 40) {
412: throw new IOException(
413: "Invalid XMLDSIG format of DSA signature");
414: }
415:
416: int i;
417:
418: for (i = 20; (i > 0) && (xmldsigBytes[20 - i] == 0); i--)
419: ;
420:
421: int j = i;
422:
423: if (xmldsigBytes[20 - i] < 0) {
424: j += 1;
425: }
426:
427: int k;
428:
429: for (k = 20; (k > 0) && (xmldsigBytes[40 - k] == 0); k--)
430: ;
431:
432: int l = k;
433:
434: if (xmldsigBytes[40 - k] < 0) {
435: l += 1;
436: }
437:
438: byte asn1Bytes[] = new byte[6 + j + l];
439:
440: asn1Bytes[0] = 48;
441: asn1Bytes[1] = (byte) (4 + j + l);
442: asn1Bytes[2] = 2;
443: asn1Bytes[3] = (byte) j;
444:
445: System.arraycopy(xmldsigBytes, 20 - i, asn1Bytes, (4 + j) - i,
446: i);
447:
448: asn1Bytes[4 + j] = 2;
449: asn1Bytes[5 + j] = (byte) l;
450:
451: System.arraycopy(xmldsigBytes, 40 - k, asn1Bytes, (6 + j + l)
452: - k, k);
453:
454: return asn1Bytes;
455: }
456:
457: static final class SHA1withRSA extends DOMSignatureMethod {
458: SHA1withRSA(AlgorithmParameterSpec params)
459: throws InvalidAlgorithmParameterException {
460: super (params);
461: }
462:
463: SHA1withRSA(Element dmElem) throws MarshalException {
464: super (dmElem);
465: }
466:
467: public String getAlgorithm() {
468: return SignatureMethod.RSA_SHA1;
469: }
470:
471: String getSignatureAlgorithm() {
472: return "SHA1withRSA";
473: }
474: }
475:
476: static final class SHA256withRSA extends DOMSignatureMethod {
477: SHA256withRSA(AlgorithmParameterSpec params)
478: throws InvalidAlgorithmParameterException {
479: super (params);
480: }
481:
482: SHA256withRSA(Element dmElem) throws MarshalException {
483: super (dmElem);
484: }
485:
486: public String getAlgorithm() {
487: return RSA_SHA256;
488: }
489:
490: String getSignatureAlgorithm() {
491: return "SHA256withRSA";
492: }
493: }
494:
495: static final class SHA384withRSA extends DOMSignatureMethod {
496: SHA384withRSA(AlgorithmParameterSpec params)
497: throws InvalidAlgorithmParameterException {
498: super (params);
499: }
500:
501: SHA384withRSA(Element dmElem) throws MarshalException {
502: super (dmElem);
503: }
504:
505: public String getAlgorithm() {
506: return RSA_SHA384;
507: }
508:
509: String getSignatureAlgorithm() {
510: return "SHA384withRSA";
511: }
512: }
513:
514: static final class SHA512withRSA extends DOMSignatureMethod {
515: SHA512withRSA(AlgorithmParameterSpec params)
516: throws InvalidAlgorithmParameterException {
517: super (params);
518: }
519:
520: SHA512withRSA(Element dmElem) throws MarshalException {
521: super (dmElem);
522: }
523:
524: public String getAlgorithm() {
525: return RSA_SHA512;
526: }
527:
528: String getSignatureAlgorithm() {
529: return "SHA512withRSA";
530: }
531: }
532:
533: static final class SHA1withDSA extends DOMSignatureMethod {
534: SHA1withDSA(AlgorithmParameterSpec params)
535: throws InvalidAlgorithmParameterException {
536: super (params);
537: }
538:
539: SHA1withDSA(Element dmElem) throws MarshalException {
540: super (dmElem);
541: }
542:
543: public String getAlgorithm() {
544: return SignatureMethod.DSA_SHA1;
545: }
546:
547: String getSignatureAlgorithm() {
548: return "SHA1withDSA";
549: }
550: }
551: }
|