diff options
Diffstat (limited to 'libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java')
-rw-r--r-- | libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java new file mode 100644 index 000000000..c0940855d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java @@ -0,0 +1,487 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; + +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.util.Strings; + +/** + * Generator for PGP Signatures. + */ +public class PGPSignatureGenerator +{ + private SignatureSubpacket[] unhashed = new SignatureSubpacket[0]; + private SignatureSubpacket[] hashed = new SignatureSubpacket[0]; + private OutputStream sigOut; + private PGPContentSignerBuilder contentSignerBuilder; + private PGPContentSigner contentSigner; + private int sigType; + private byte lastb; + private int providedKeyAlgorithm = -1; + + /** + * Create a signature generator built on the passed in contentSignerBuilder. + * + * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures. + */ + public PGPSignatureGenerator( + PGPContentSignerBuilder contentSignerBuilder) + { + this.contentSignerBuilder = contentSignerBuilder; + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + * @deprecated use init() method + */ + public void initSign( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + */ + public void init( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @param random + * @throws PGPException + * @deprecated random parameter now ignored. + */ + public void initSign( + int signatureType, + PGPPrivateKey key, + SecureRandom random) + throws PGPException + { + initSign(signatureType, key); + } + + public void update( + byte b) + throws PGPSignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] b) + throws PGPSignatureException + { + this.update(b, 0, b.length); + } + + public void update( + byte[] b, + int off, + int len) + throws PGPSignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + this.update(b[i]); + } + } + else + { + blockUpdate(b, off, len); + } + } + + private void byteUpdate(byte b) + throws PGPSignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws PGPSignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + public void setHashedSubpackets( + PGPSignatureSubpacketVector hashedPcks) + { + if (hashedPcks == null) + { + hashed = new SignatureSubpacket[0]; + return; + } + + hashed = hashedPcks.toSubpacketArray(); + } + + public void setUnhashedSubpackets( + PGPSignatureSubpacketVector unhashedPcks) + { + if (unhashedPcks == null) + { + unhashed = new SignatureSubpacket[0]; + return; + } + + unhashed = unhashedPcks.toSubpacketArray(); + } + + /** + * Return the one pass header associated with the current signature. + * + * @param isNested + * @return PGPOnePassSignature + * @throws PGPException + */ + public PGPOnePassSignature generateOnePassVersion( + boolean isNested) + throws PGPException + { + return new PGPOnePassSignature(new OnePassSignaturePacket(sigType, contentSigner.getHashAlgorithm(), contentSigner.getKeyAlgorithm(), contentSigner.getKeyID(), isNested)); + } + + /** + * Return a signature object containing the current signature state. + * + * @return PGPSignature + * @throws PGPException + */ + public PGPSignature generate() + throws PGPException + { + MPInteger[] sigValues; + int version = 4; + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + SignatureSubpacket[] hPkts, unhPkts; + + if (!packetPresent(hashed, SignatureSubpacketTags.CREATION_TIME)) + { + hPkts = insertSubpacket(hashed, new SignatureCreationTime(false, new Date())); + } + else + { + hPkts = hashed; + } + + if (!packetPresent(hashed, SignatureSubpacketTags.ISSUER_KEY_ID) && !packetPresent(unhashed, SignatureSubpacketTags.ISSUER_KEY_ID)) + { + unhPkts = insertSubpacket(unhashed, new IssuerKeyID(false, contentSigner.getKeyID())); + } + else + { + unhPkts = unhashed; + } + + try + { + sOut.write((byte)version); + sOut.write((byte)sigType); + sOut.write((byte)contentSigner.getKeyAlgorithm()); + sOut.write((byte)contentSigner.getHashAlgorithm()); + + ByteArrayOutputStream hOut = new ByteArrayOutputStream(); + + for (int i = 0; i != hPkts.length; i++) + { + hPkts[i].encode(hOut); + } + + byte[] data = hOut.toByteArray(); + + sOut.write((byte)(data.length >> 8)); + sOut.write((byte)data.length); + sOut.write(data); + } + catch (IOException e) + { + throw new PGPException("exception encoding hashed data.", e); + } + + byte[] hData = sOut.toByteArray(); + + sOut.write((byte)version); + sOut.write((byte)0xff); + sOut.write((byte)(hData.length >> 24)); + sOut.write((byte)(hData.length >> 16)); + sOut.write((byte)(hData.length >> 8)); + sOut.write((byte)(hData.length)); + + byte[] trailer = sOut.toByteArray(); + + blockUpdate(trailer, 0, trailer.length); + + if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN + || contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) // an RSA signature + { + sigValues = new MPInteger[1]; + sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature())); + } + else + { + sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature()); + } + + byte[] digest = contentSigner.getDigest(); + byte[] fingerPrint = new byte[2]; + + fingerPrint[0] = digest[0]; + fingerPrint[1] = digest[1]; + + return new PGPSignature(new SignaturePacket(sigType, contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(), contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, sigValues)); + } + + /** + * Generate a certification for the passed in id and key. + * + * @param id the id we are certifying against the public key. + * @param pubKey the key we are certifying against the id. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + String id, + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(pubKey); + + // + // hash in the id + // + updateWithIdData(0xb4, Strings.toUTF8ByteArray(id)); + + return this.generate(); + } + + /** + * Generate a certification for the passed in userAttributes + * @param userAttributes the id we are certifying against the public key. + * @param pubKey the key we are certifying against the id. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPUserAttributeSubpacketVector userAttributes, + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(pubKey); + + // + // hash in the attributes + // + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + UserAttributeSubpacket[] packets = userAttributes.toSubpacketArray(); + for (int i = 0; i != packets.length; i++) + { + packets[i].encode(bOut); + } + updateWithIdData(0xd1, bOut.toByteArray()); + } + catch (IOException e) + { + throw new PGPException("cannot encode subpacket array", e); + } + + return this.generate(); + } + + /** + * Generate a certification for the passed in key against the passed in + * master key. + * + * @param masterKey the key we are certifying against. + * @param pubKey the key we are certifying. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPPublicKey masterKey, + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(masterKey); + updateWithPublicKey(pubKey); + + return this.generate(); + } + + /** + * Generate a certification, such as a revocation, for the passed in key. + * + * @param pubKey the key we are certifying. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(pubKey); + + return this.generate(); + } + + private byte[] getEncodedPublicKey( + PGPPublicKey pubKey) + throws PGPException + { + byte[] keyBytes; + + try + { + keyBytes = pubKey.publicPk.getEncodedContents(); + } + catch (IOException e) + { + throw new PGPException("exception preparing key.", e); + } + + return keyBytes; + } + + private boolean packetPresent( + SignatureSubpacket[] packets, + int type) + { + for (int i = 0; i != packets.length; i++) + { + if (packets[i].getType() == type) + { + return true; + } + } + + return false; + } + + private SignatureSubpacket[] insertSubpacket( + SignatureSubpacket[] packets, + SignatureSubpacket subpacket) + { + SignatureSubpacket[] tmp = new SignatureSubpacket[packets.length + 1]; + + tmp[0] = subpacket; + System.arraycopy(packets, 0, tmp, 1, packets.length); + + return tmp; + } + + private void updateWithIdData(int header, byte[] idBytes) + throws PGPSignatureException + { + this.update((byte)header); + this.update((byte)(idBytes.length >> 24)); + this.update((byte)(idBytes.length >> 16)); + this.update((byte)(idBytes.length >> 8)); + this.update((byte)(idBytes.length)); + this.update(idBytes); + } + + private void updateWithPublicKey(PGPPublicKey key) + throws PGPException + { + byte[] keyBytes = getEncodedPublicKey(key); + + this.update((byte)0x99); + this.update((byte)(keyBytes.length >> 8)); + this.update((byte)(keyBytes.length)); + this.update(keyBytes); + } +} |