001: package org.bouncycastle.openpgp;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.ASN1OutputStream;
005: import org.bouncycastle.asn1.DERInteger;
006: import org.bouncycastle.asn1.DERSequence;
007: import org.bouncycastle.bcpg.BCPGInputStream;
008: import org.bouncycastle.bcpg.BCPGOutputStream;
009: import org.bouncycastle.bcpg.MPInteger;
010: import org.bouncycastle.bcpg.SignaturePacket;
011: import org.bouncycastle.bcpg.SignatureSubpacket;
012: import org.bouncycastle.bcpg.TrustPacket;
013:
014: import java.io.ByteArrayOutputStream;
015: import java.io.IOException;
016: import java.io.OutputStream;
017: import java.security.InvalidKeyException;
018: import java.security.NoSuchProviderException;
019: import java.security.Signature;
020: import java.security.SignatureException;
021: import java.util.Date;
022:
023: /**
024: *A PGP signature object.
025: */
026: public class PGPSignature {
027: public static final int BINARY_DOCUMENT = 0x00;
028: public static final int CANONICAL_TEXT_DOCUMENT = 0x01;
029: public static final int STAND_ALONE = 0x02;
030:
031: public static final int DEFAULT_CERTIFICATION = 0x10;
032: public static final int NO_CERTIFICATION = 0x11;
033: public static final int CASUAL_CERTIFICATION = 0x12;
034: public static final int POSITIVE_CERTIFICATION = 0x13;
035:
036: public static final int SUBKEY_BINDING = 0x18;
037: public static final int DIRECT_KEY = 0x1f;
038: public static final int KEY_REVOCATION = 0x20;
039: public static final int SUBKEY_REVOCATION = 0x28;
040: public static final int CERTIFICATION_REVOCATION = 0x30;
041: public static final int TIMESTAMP = 0x40;
042:
043: private SignaturePacket sigPck;
044: private Signature sig;
045: private int signatureType;
046: private TrustPacket trustPck;
047:
048: private byte lastb;
049:
050: PGPSignature(BCPGInputStream pIn) throws IOException, PGPException {
051: this ((SignaturePacket) pIn.readPacket());
052: }
053:
054: PGPSignature(SignaturePacket sigPacket) throws PGPException {
055: sigPck = sigPacket;
056: signatureType = sigPck.getSignatureType();
057: trustPck = null;
058: }
059:
060: PGPSignature(SignaturePacket sigPacket, TrustPacket trustPacket)
061: throws PGPException {
062: this (sigPacket);
063:
064: this .trustPck = trustPacket;
065: }
066:
067: private void getSig(String provider) throws PGPException {
068: try {
069: this .sig = Signature.getInstance(PGPUtil
070: .getSignatureName(sigPck.getKeyAlgorithm(), sigPck
071: .getHashAlgorithm()), provider);
072: } catch (Exception e) {
073: throw new PGPException("can't set up signature object.", e);
074: }
075: }
076:
077: /**
078: * Return the OpenPGP version number for this signature.
079: *
080: * @return signature version number.
081: */
082: public int getVersion() {
083: return sigPck.getVersion();
084: }
085:
086: /**
087: * Return the key algorithm associated with this signature.
088: * @return signature key algorithm.
089: */
090: public int getKeyAlgorithm() {
091: return sigPck.getKeyAlgorithm();
092: }
093:
094: /**
095: * Return the hash algorithm associated with this signature.
096: * @return signature hash algorithm.
097: */
098: public int getHashAlgorithm() {
099: return sigPck.getHashAlgorithm();
100: }
101:
102: public void initVerify(PGPPublicKey pubKey, String provider)
103: throws NoSuchProviderException, PGPException {
104: if (sig == null) {
105: getSig(provider);
106: }
107:
108: try {
109: sig.initVerify(pubKey.getKey(provider));
110: } catch (InvalidKeyException e) {
111: throw new PGPException("invalid key.", e);
112: }
113:
114: lastb = 0;
115: }
116:
117: public void update(byte b) throws SignatureException {
118: if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) {
119: if (b == '\r') {
120: sig.update((byte) '\r');
121: sig.update((byte) '\n');
122: } else if (b == '\n') {
123: if (lastb != '\r') {
124: sig.update((byte) '\r');
125: sig.update((byte) '\n');
126: }
127: } else {
128: sig.update(b);
129: }
130:
131: lastb = b;
132: } else {
133: sig.update(b);
134: }
135: }
136:
137: public void update(byte[] bytes) throws SignatureException {
138: this .update(bytes, 0, bytes.length);
139: }
140:
141: public void update(byte[] bytes, int off, int length)
142: throws SignatureException {
143: if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) {
144: int finish = off + length;
145:
146: for (int i = off; i != finish; i++) {
147: this .update(bytes[i]);
148: }
149: } else {
150: sig.update(bytes, off, length);
151: }
152: }
153:
154: public boolean verify() throws PGPException, SignatureException {
155: sig.update(this .getSignatureTrailer());
156:
157: return sig.verify(this .getSignature());
158: }
159:
160: /**
161: * Verify the signature as certifying the passed in public key as associated
162: * with the passed in id.
163: *
164: * @param id id the key was stored under
165: * @param key the key to be verified.
166: * @return true if the signature matches, false otherwise.
167: * @throws PGPException
168: * @throws SignatureException
169: */
170: public boolean verifyCertification(String id, PGPPublicKey key)
171: throws PGPException, SignatureException {
172: byte[] keyBytes = getEncodedPublicKey(key);
173:
174: this .update((byte) 0x99);
175: this .update((byte) (keyBytes.length >> 8));
176: this .update((byte) (keyBytes.length));
177: this .update(keyBytes);
178:
179: //
180: // hash in the id
181: //
182: byte[] idBytes = new byte[id.length()];
183:
184: for (int i = 0; i != idBytes.length; i++) {
185: idBytes[i] = (byte) id.charAt(i);
186: }
187:
188: this .update((byte) 0xb4);
189: this .update((byte) (idBytes.length >> 24));
190: this .update((byte) (idBytes.length >> 16));
191: this .update((byte) (idBytes.length >> 8));
192: this .update((byte) (idBytes.length));
193: this .update(idBytes);
194:
195: this .update(sigPck.getSignatureTrailer());
196:
197: return sig.verify(this .getSignature());
198: }
199:
200: /**
201: * Verify a certification for the passed in key against the passed in
202: * master key.
203: *
204: * @param masterKey the key we are verifying against.
205: * @param pubKey the key we are verifying.
206: * @return true if the certification is valid, false otherwise.
207: * @throws SignatureException
208: * @throws PGPException
209: */
210: public boolean verifyCertification(PGPPublicKey masterKey,
211: PGPPublicKey pubKey) throws SignatureException,
212: PGPException {
213: byte[] keyBytes = getEncodedPublicKey(masterKey);
214:
215: this .update((byte) 0x99);
216: this .update((byte) (keyBytes.length >> 8));
217: this .update((byte) (keyBytes.length));
218: this .update(keyBytes);
219:
220: keyBytes = getEncodedPublicKey(pubKey);
221:
222: this .update((byte) 0x99);
223: this .update((byte) (keyBytes.length >> 8));
224: this .update((byte) (keyBytes.length));
225: this .update(keyBytes);
226:
227: this .update(sigPck.getSignatureTrailer());
228:
229: return sig.verify(this .getSignature());
230: }
231:
232: /**
233: * Verify a key certification, such as a revocation, for the passed in key.
234: *
235: * @param pubKey the key we are checking.
236: * @return true if the certification is valid, false otherwise.
237: * @throws SignatureException
238: * @throws PGPException
239: */
240: public boolean verifyCertification(PGPPublicKey pubKey)
241: throws SignatureException, PGPException {
242: if (this .getSignatureType() != KEY_REVOCATION
243: && this .getSignatureType() != SUBKEY_REVOCATION) {
244: throw new IllegalStateException(
245: "signature is not a key signature");
246: }
247:
248: byte[] keyBytes = getEncodedPublicKey(pubKey);
249:
250: this .update((byte) 0x99);
251: this .update((byte) (keyBytes.length >> 8));
252: this .update((byte) (keyBytes.length));
253: this .update(keyBytes);
254:
255: this .update(sigPck.getSignatureTrailer());
256:
257: return sig.verify(this .getSignature());
258: }
259:
260: public int getSignatureType() {
261: return sigPck.getSignatureType();
262: }
263:
264: /**
265: * Return the id of the key that created the signature.
266: * @return keyID of the signatures corresponding key.
267: */
268: public long getKeyID() {
269: return sigPck.getKeyID();
270: }
271:
272: /**
273: * Return the creation time of the signature.
274: *
275: * @return the signature creation time.
276: */
277: public Date getCreationTime() {
278: return new Date(sigPck.getCreationTime());
279: }
280:
281: public byte[] getSignatureTrailer() {
282: return sigPck.getSignatureTrailer();
283: }
284:
285: public PGPSignatureSubpacketVector getHashedSubPackets() {
286: return createSubpacketVector(sigPck.getHashedSubPackets());
287: }
288:
289: public PGPSignatureSubpacketVector getUnhashedSubPackets() {
290: return createSubpacketVector(sigPck.getUnhashedSubPackets());
291: }
292:
293: private PGPSignatureSubpacketVector createSubpacketVector(
294: SignatureSubpacket[] pcks) {
295: if (pcks != null) {
296: return new PGPSignatureSubpacketVector(pcks);
297: }
298:
299: return null;
300: }
301:
302: public byte[] getSignature() throws PGPException {
303: MPInteger[] sigValues = sigPck.getSignature();
304: byte[] signature;
305:
306: if (sigValues != null) {
307: if (sigValues.length == 1) // an RSA signature
308: {
309: byte[] sBytes = sigValues[0].getValue().toByteArray();
310:
311: if (sBytes[0] == 0) {
312: signature = new byte[sBytes.length - 1];
313: System.arraycopy(sBytes, 1, signature, 0,
314: signature.length);
315: } else {
316: signature = sBytes;
317: }
318: } else {
319: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
320: ASN1OutputStream aOut = new ASN1OutputStream(bOut);
321:
322: try {
323: ASN1EncodableVector v = new ASN1EncodableVector();
324:
325: v.add(new DERInteger(sigValues[0].getValue()));
326: v.add(new DERInteger(sigValues[1].getValue()));
327:
328: aOut.writeObject(new DERSequence(v));
329: } catch (IOException e) {
330: throw new PGPException(
331: "exception encoding DSA sig.", e);
332: }
333:
334: signature = bOut.toByteArray();
335: }
336: } else {
337: signature = sigPck.getSignatureBytes();
338: }
339:
340: return signature;
341: }
342:
343: public byte[] getEncoded() throws IOException {
344: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
345:
346: this .encode(bOut);
347:
348: return bOut.toByteArray();
349: }
350:
351: public void encode(OutputStream outStream) throws IOException {
352: BCPGOutputStream out;
353:
354: if (outStream instanceof BCPGOutputStream) {
355: out = (BCPGOutputStream) outStream;
356: } else {
357: out = new BCPGOutputStream(outStream);
358: }
359:
360: out.writePacket(sigPck);
361: if (trustPck != null) {
362: out.writePacket(trustPck);
363: }
364: }
365:
366: private byte[] getEncodedPublicKey(PGPPublicKey pubKey)
367: throws PGPException {
368: byte[] keyBytes;
369:
370: try {
371: keyBytes = pubKey.publicPk.getEncodedContents();
372: } catch (IOException e) {
373: throw new PGPException("exception preparing key.", e);
374: }
375:
376: return keyBytes;
377: }
378: }
|