001: package org.bouncycastle.openpgp;
002:
003: import org.bouncycastle.bcpg.MPInteger;
004: import org.bouncycastle.bcpg.OnePassSignaturePacket;
005: import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
006: import org.bouncycastle.bcpg.SignaturePacket;
007: import org.bouncycastle.bcpg.SignatureSubpacket;
008: import org.bouncycastle.bcpg.SignatureSubpacketTags;
009: import org.bouncycastle.bcpg.sig.IssuerKeyID;
010: import org.bouncycastle.bcpg.sig.SignatureCreationTime;
011:
012: import java.io.ByteArrayOutputStream;
013: import java.io.IOException;
014: import java.math.BigInteger;
015: import java.security.InvalidKeyException;
016: import java.security.MessageDigest;
017: import java.security.NoSuchAlgorithmException;
018: import java.security.NoSuchProviderException;
019: import java.security.Signature;
020: import java.security.SignatureException;
021: import java.util.Date;
022:
023: /**
024: * Generator for PGP Signatures.
025: */
026: public class PGPSignatureGenerator {
027: private int keyAlgorithm;
028: private int hashAlgorithm;
029: private PGPPrivateKey privKey;
030: private Signature sig;
031: private MessageDigest dig;
032: private int signatureType;
033:
034: private byte lastb;
035:
036: SignatureSubpacket[] unhashed = new SignatureSubpacket[0];
037: SignatureSubpacket[] hashed = new SignatureSubpacket[0];
038:
039: /**
040: * Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.
041: *
042: * @param keyAlgorithm keyAlgorithm to use for signing
043: * @param hashAlgorithm algorithm to use for digest
044: * @param provider provider to use for digest algorithm
045: * @throws NoSuchAlgorithmException
046: * @throws NoSuchProviderException
047: * @throws PGPException
048: */
049: public PGPSignatureGenerator(int keyAlgorithm, int hashAlgorithm,
050: String provider) throws NoSuchAlgorithmException,
051: NoSuchProviderException, PGPException {
052: this (keyAlgorithm, provider, hashAlgorithm, provider);
053: }
054:
055: /**
056: * Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.
057: *
058: * @param keyAlgorithm keyAlgorithm to use for signing
059: * @param sigProvider provider to use for signature generation
060: * @param hashAlgorithm algorithm to use for digest
061: * @param provider provider to use for digest algorithm
062: * @throws NoSuchAlgorithmException
063: * @throws NoSuchProviderException
064: * @throws PGPException
065: */
066: public PGPSignatureGenerator(int keyAlgorithm, String sigProvider,
067: int hashAlgorithm, String provider)
068: throws NoSuchAlgorithmException, NoSuchProviderException,
069: PGPException {
070: this .keyAlgorithm = keyAlgorithm;
071: this .hashAlgorithm = hashAlgorithm;
072:
073: dig = PGPUtil.getDigestInstance(PGPUtil
074: .getDigestName(hashAlgorithm), provider);
075: sig = Signature.getInstance(PGPUtil.getSignatureName(
076: keyAlgorithm, hashAlgorithm), sigProvider);
077: }
078:
079: /**
080: * Initialise the generator for signing.
081: *
082: * @param signatureType
083: * @param key
084: * @throws PGPException
085: */
086: public void initSign(int signatureType, PGPPrivateKey key)
087: throws PGPException {
088: this .privKey = key;
089: this .signatureType = signatureType;
090:
091: try {
092: sig.initSign(key.getKey());
093: } catch (InvalidKeyException e) {
094: throw new PGPException("invalid key.", e);
095: }
096:
097: dig.reset();
098: lastb = 0;
099: }
100:
101: public void update(byte b) throws SignatureException {
102: if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) {
103: if (b == '\r') {
104: sig.update((byte) '\r');
105: sig.update((byte) '\n');
106: dig.update((byte) '\r');
107: dig.update((byte) '\n');
108: } else if (b == '\n') {
109: if (lastb != '\r') {
110: sig.update((byte) '\r');
111: sig.update((byte) '\n');
112: dig.update((byte) '\r');
113: dig.update((byte) '\n');
114: }
115: } else {
116: sig.update(b);
117: dig.update(b);
118: }
119:
120: lastb = b;
121: } else {
122: sig.update(b);
123: dig.update(b);
124: }
125: }
126:
127: public void update(byte[] b) throws SignatureException {
128: this .update(b, 0, b.length);
129: }
130:
131: public void update(byte[] b, int off, int len)
132: throws SignatureException {
133: if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) {
134: int finish = off + len;
135:
136: for (int i = off; i != finish; i++) {
137: this .update(b[i]);
138: }
139: } else {
140: sig.update(b, off, len);
141: dig.update(b, off, len);
142: }
143: }
144:
145: public void setHashedSubpackets(
146: PGPSignatureSubpacketVector hashedPcks) {
147: if (hashedPcks == null) {
148: hashed = new SignatureSubpacket[0];
149: return;
150: }
151:
152: hashed = hashedPcks.toSubpacketArray();
153: }
154:
155: public void setUnhashedSubpackets(
156: PGPSignatureSubpacketVector unhashedPcks) {
157: if (unhashedPcks == null) {
158: unhashed = new SignatureSubpacket[0];
159: return;
160: }
161:
162: unhashed = unhashedPcks.toSubpacketArray();
163: }
164:
165: /**
166: * Return the one pass header associated with the current signature.
167: *
168: * @param isNested
169: * @return PGPOnePassSignature
170: * @throws PGPException
171: */
172: public PGPOnePassSignature generateOnePassVersion(boolean isNested)
173: throws PGPException {
174: return new PGPOnePassSignature(new OnePassSignaturePacket(
175: signatureType, hashAlgorithm, keyAlgorithm, privKey
176: .getKeyID(), isNested));
177: }
178:
179: /**
180: * Return a signature object containing the current signature state.
181: *
182: * @return PGPSignature
183: * @throws PGPException
184: * @throws SignatureException
185: */
186: public PGPSignature generate() throws PGPException,
187: SignatureException {
188: MPInteger[] sigValues;
189: int version = 4;
190: ByteArrayOutputStream sOut = new ByteArrayOutputStream();
191: SignatureSubpacket[] hPkts, unhPkts;
192:
193: if (!packetPresent(hashed, SignatureSubpacketTags.CREATION_TIME)) {
194: hPkts = insertSubpacket(hashed, new SignatureCreationTime(
195: false, new Date()));
196: } else {
197: hPkts = hashed;
198: }
199:
200: if (!packetPresent(hashed, SignatureSubpacketTags.ISSUER_KEY_ID)
201: && !packetPresent(unhashed,
202: SignatureSubpacketTags.ISSUER_KEY_ID)) {
203: unhPkts = insertSubpacket(unhashed, new IssuerKeyID(false,
204: privKey.getKeyID()));
205: } else {
206: unhPkts = unhashed;
207: }
208:
209: try {
210: sOut.write((byte) version);
211: sOut.write((byte) signatureType);
212: sOut.write((byte) keyAlgorithm);
213: sOut.write((byte) hashAlgorithm);
214:
215: ByteArrayOutputStream hOut = new ByteArrayOutputStream();
216:
217: for (int i = 0; i != hPkts.length; i++) {
218: hPkts[i].encode(hOut);
219: }
220:
221: byte[] data = hOut.toByteArray();
222:
223: sOut.write((byte) (data.length >> 8));
224: sOut.write((byte) data.length);
225: sOut.write(data);
226: } catch (IOException e) {
227: throw new PGPException("exception encoding hashed data.", e);
228: }
229:
230: byte[] hData = sOut.toByteArray();
231:
232: sOut.write((byte) version);
233: sOut.write((byte) 0xff);
234: sOut.write((byte) (hData.length >> 24));
235: sOut.write((byte) (hData.length >> 16));
236: sOut.write((byte) (hData.length >> 8));
237: sOut.write((byte) (hData.length));
238:
239: byte[] trailer = sOut.toByteArray();
240:
241: sig.update(trailer);
242: dig.update(trailer);
243:
244: if (keyAlgorithm == PublicKeyAlgorithmTags.RSA_SIGN
245: || keyAlgorithm == PublicKeyAlgorithmTags.RSA_GENERAL) // an RSA signature
246: {
247: sigValues = new MPInteger[1];
248: sigValues[0] = new MPInteger(new BigInteger(1, sig.sign()));
249: } else {
250: sigValues = PGPUtil.dsaSigToMpi(sig.sign());
251: }
252:
253: byte[] digest = dig.digest();
254: byte[] fingerPrint = new byte[2];
255:
256: fingerPrint[0] = digest[0];
257: fingerPrint[1] = digest[1];
258:
259: return new PGPSignature(new SignaturePacket(signatureType,
260: privKey.getKeyID(), keyAlgorithm, hashAlgorithm, hPkts,
261: unhPkts, fingerPrint, sigValues));
262: }
263:
264: /**
265: * Generate a certification for the passed in id and key.
266: *
267: * @param id the id we are certifying against the public key.
268: * @param pubKey the key we are certifying against the id.
269: * @return the certification.
270: * @throws SignatureException
271: * @throws PGPException
272: */
273: public PGPSignature generateCertification(String id,
274: PGPPublicKey pubKey) throws SignatureException,
275: PGPException {
276: byte[] keyBytes = getEncodedPublicKey(pubKey);
277:
278: this .update((byte) 0x99);
279: this .update((byte) (keyBytes.length >> 8));
280: this .update((byte) (keyBytes.length));
281: this .update(keyBytes);
282:
283: //
284: // hash in the id
285: //
286: byte[] idBytes = new byte[id.length()];
287:
288: for (int i = 0; i != idBytes.length; i++) {
289: idBytes[i] = (byte) id.charAt(i);
290: }
291:
292: this .update((byte) 0xb4);
293: this .update((byte) (idBytes.length >> 24));
294: this .update((byte) (idBytes.length >> 16));
295: this .update((byte) (idBytes.length >> 8));
296: this .update((byte) (idBytes.length));
297: this .update(idBytes);
298:
299: return this .generate();
300: }
301:
302: /**
303: * Generate a certification for the passed in key against the passed in
304: * master key.
305: *
306: * @param masterKey the key we are certifying against.
307: * @param pubKey the key we are certifying.
308: * @return the certification.
309: * @throws SignatureException
310: * @throws PGPException
311: */
312: public PGPSignature generateCertification(PGPPublicKey masterKey,
313: PGPPublicKey pubKey) throws SignatureException,
314: PGPException {
315: byte[] keyBytes = getEncodedPublicKey(masterKey);
316:
317: this .update((byte) 0x99);
318: this .update((byte) (keyBytes.length >> 8));
319: this .update((byte) (keyBytes.length));
320: this .update(keyBytes);
321:
322: keyBytes = getEncodedPublicKey(pubKey);
323:
324: this .update((byte) 0x99);
325: this .update((byte) (keyBytes.length >> 8));
326: this .update((byte) (keyBytes.length));
327: this .update(keyBytes);
328:
329: return this .generate();
330: }
331:
332: /**
333: * Generate a certification, such as a revocation, for the passed in key.
334: *
335: * @param pubKey the key we are certifying.
336: * @return the certification.
337: * @throws SignatureException
338: * @throws PGPException
339: */
340: public PGPSignature generateCertification(PGPPublicKey pubKey)
341: throws SignatureException, PGPException {
342: byte[] keyBytes = getEncodedPublicKey(pubKey);
343:
344: this .update((byte) 0x99);
345: this .update((byte) (keyBytes.length >> 8));
346: this .update((byte) (keyBytes.length));
347: this .update(keyBytes);
348:
349: return this .generate();
350: }
351:
352: private byte[] getEncodedPublicKey(PGPPublicKey pubKey)
353: throws PGPException {
354: byte[] keyBytes;
355:
356: try {
357: keyBytes = pubKey.publicPk.getEncodedContents();
358: } catch (IOException e) {
359: throw new PGPException("exception preparing key.", e);
360: }
361:
362: return keyBytes;
363: }
364:
365: private boolean packetPresent(SignatureSubpacket[] packets, int type) {
366: for (int i = 0; i != packets.length; i++) {
367: if (packets[i].getType() == type) {
368: return true;
369: }
370: }
371:
372: return false;
373: }
374:
375: private SignatureSubpacket[] insertSubpacket(
376: SignatureSubpacket[] packets, SignatureSubpacket subpacket) {
377: SignatureSubpacket[] tmp = new SignatureSubpacket[packets.length + 1];
378:
379: tmp[0] = subpacket;
380: System.arraycopy(packets, 0, tmp, 1, packets.length);
381:
382: return tmp;
383: }
384: }
|