001    package org.apache.commons.openpgp;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.security.Security;
023    import java.security.SignatureException;
024    
025    import org.bouncycastle.jce.provider.BouncyCastleProvider;
026    import org.bouncycastle.openpgp.PGPCompressedData;
027    import org.bouncycastle.openpgp.PGPException;
028    import org.bouncycastle.openpgp.PGPObjectFactory;
029    import org.bouncycastle.openpgp.PGPPublicKey;
030    import org.bouncycastle.openpgp.PGPSignature;
031    import org.bouncycastle.openpgp.PGPSignatureList;
032    import org.bouncycastle.openpgp.PGPUtil;
033    import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
034    
035    /**
036     * Bouncy Castle implementation of the OpenPGP signer.
037     * 
038     * @author <a href="mailto:brett@apache.org">Brett Porter</a>
039     */
040    public class BouncyCastleOpenPgpStreamingSignatureVerifier
041        implements OpenPgpStreamingSignatureVerifier
042    {
043        private PGPSignature sig;
044    
045        public BouncyCastleOpenPgpStreamingSignatureVerifier( InputStream signature, KeyRing keyRing )
046            throws OpenPgpException, IOException
047        {
048            init( signature, keyRing );
049        }
050    
051        private void init( InputStream signature, KeyRing keyRing )
052            throws OpenPgpException, IOException
053        {
054            // TODO: better location for this?
055            Security.addProvider( new BouncyCastleProvider() );
056    
057            try
058            {
059                signature = PGPUtil.getDecoderStream( signature );
060    
061                PGPPublicKey key = null;
062                while ( key == null && signature.available() > 0 )
063                {
064                    PGPObjectFactory pgpFact = new PGPObjectFactory( signature );
065    
066                    PGPSignatureList p3;
067    
068                    Object o = pgpFact.nextObject();
069                    if ( o == null )
070                    {
071                        break;
072                    }
073                    
074                    if ( o instanceof PGPCompressedData )
075                    {
076                        PGPCompressedData c1 = (PGPCompressedData) o;
077    
078                        pgpFact = new PGPObjectFactory( c1.getDataStream() );
079    
080                        p3 = (PGPSignatureList) pgpFact.nextObject();
081                    }
082                    else
083                    {
084                        p3 = (PGPSignatureList) o;
085                    }
086    
087                    for ( int i = 0; i < p3.size(); i++ )
088                    {
089                        sig = p3.get( i );
090                        key = keyRing.getPublicKey( sig.getKeyID() );
091                        if ( key != null )
092                        {
093                            break;
094                        }
095                        else
096                        {
097                            // TODO: log them all
098                        }
099                    }
100    
101                }
102    
103                if ( key == null )
104                {
105                    throw new UnknownKeyException( "Unable to find key with key ID '"
106                        + Long.toHexString( sig.getKeyID() ).toUpperCase() + "' in public key ring" );
107                }
108    
109                sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), key);
110            }
111            catch ( PGPException e )
112            {
113                // TODO: more details
114                throw new OpenPgpException( "Error calculating detached signature", e );
115            }
116        }
117    
118        public void update( byte[] buf )
119            throws OpenPgpException
120        {
121            update( buf, 0, buf.length );
122        }
123    
124        public void update( byte[] buf, int offset, int length )
125            throws OpenPgpException
126        {
127            try
128            {
129                sig.update( buf, offset, length );
130            }
131            catch ( SignatureException e )
132            {
133                // TODO: more details
134                throw new OpenPgpException( "Error calculating detached signature", e );
135            }
136        }
137    
138        public SignatureStatus finish()
139            throws OpenPgpException, IOException
140        {
141            try
142            {
143                if ( sig.verify() )
144                {
145                    // TODO: how do we assess trust?
146                    return SignatureStatus.VALID_UNTRUSTED;
147                }
148                else
149                {
150                    return SignatureStatus.INVALID;
151                }
152            }
153            catch ( PGPException e )
154            {
155                // TODO: more details
156                throw new OpenPgpException( "Error calculating detached signature", e );
157            }
158            catch ( SignatureException e )
159            {
160                // TODO: more details
161                throw new OpenPgpException( "Error calculating detached signature", e );
162            }
163        }
164    }