using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.Date; namespace Org.BouncyCastle.Bcpg.OpenPgp { /// Generator for old style PGP V3 Signatures. // TODO Should be able to implement ISigner? public class PgpV3SignatureGenerator { private PublicKeyAlgorithmTag keyAlgorithm; private HashAlgorithmTag hashAlgorithm; private PgpPrivateKey privKey; private ISigner sig; private IDigest dig; private int signatureType; private byte lastb; /// Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. public PgpV3SignatureGenerator( PublicKeyAlgorithmTag keyAlgorithm, HashAlgorithmTag hashAlgorithm) { this.keyAlgorithm = keyAlgorithm; this.hashAlgorithm = hashAlgorithm; dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm)); sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm)); } /// Initialise the generator for signing. public void InitSign( int sigType, PgpPrivateKey key) { InitSign(sigType, key, null); } /// Initialise the generator for signing. public void InitSign( int sigType, PgpPrivateKey key, SecureRandom random) { this.privKey = key; this.signatureType = sigType; try { ICipherParameters cp = key.Key; if (random != null) { cp = new ParametersWithRandom(key.Key, random); } sig.Init(true, cp); } catch (InvalidKeyException e) { throw new PgpException("invalid key.", e); } dig.Reset(); lastb = 0; } public void Update( byte b) { if (signatureType == PgpSignature.CanonicalTextDocument) { doCanonicalUpdateByte(b); } else { doUpdateByte(b); } } private void doCanonicalUpdateByte( byte b) { if (b == '\r') { doUpdateCRLF(); } else if (b == '\n') { if (lastb != '\r') { doUpdateCRLF(); } } else { doUpdateByte(b); } lastb = b; } private void doUpdateCRLF() { doUpdateByte((byte)'\r'); doUpdateByte((byte)'\n'); } private void doUpdateByte( byte b) { sig.Update(b); dig.Update(b); } public void Update( byte[] b) { if (signatureType == PgpSignature.CanonicalTextDocument) { for (int i = 0; i != b.Length; i++) { doCanonicalUpdateByte(b[i]); } } else { sig.BlockUpdate(b, 0, b.Length); dig.BlockUpdate(b, 0, b.Length); } } public void Update( byte[] b, int off, int len) { if (signatureType == PgpSignature.CanonicalTextDocument) { int finish = off + len; for (int i = off; i != finish; i++) { doCanonicalUpdateByte(b[i]); } } else { sig.BlockUpdate(b, off, len); dig.BlockUpdate(b, off, len); } } /// Return the one pass header associated with the current signature. public PgpOnePassSignature GenerateOnePassVersion( bool isNested) { return new PgpOnePassSignature( new OnePassSignaturePacket(signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested)); } /// Return a V3 signature object containing the current signature state. public PgpSignature Generate() { long creationTime = DateTimeUtilities.CurrentUnixMs() / 1000L; byte[] hData = new byte[] { (byte) signatureType, (byte)(creationTime >> 24), (byte)(creationTime >> 16), (byte)(creationTime >> 8), (byte) creationTime }; sig.BlockUpdate(hData, 0, hData.Length); dig.BlockUpdate(hData, 0, hData.Length); byte[] sigBytes = sig.GenerateSignature(); byte[] digest = DigestUtilities.DoFinal(dig); byte[] fingerPrint = new byte[]{ digest[0], digest[1] }; // an RSA signature bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral; MPInteger[] sigValues = isRsa ? PgpUtilities.RsaSigToMpi(sigBytes) : PgpUtilities.DsaSigToMpi(sigBytes); return new PgpSignature( new SignaturePacket(3, signatureType, privKey.KeyId, keyAlgorithm, hashAlgorithm, creationTime * 1000L, fingerPrint, sigValues)); } } }