001    /*
002     * Copyright 2010 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.vafer.jdeb.signing;
017    
018    import java.io.BufferedReader;
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.io.InputStreamReader;
022    import java.io.OutputStream;
023    import java.security.NoSuchAlgorithmException;
024    import java.security.NoSuchProviderException;
025    import java.security.Security;
026    import java.security.SignatureException;
027    import java.util.Iterator;
028    
029    import org.bouncycastle.bcpg.ArmoredOutputStream;
030    import org.bouncycastle.bcpg.BCPGOutputStream;
031    import org.bouncycastle.jce.provider.BouncyCastleProvider;
032    import org.bouncycastle.openpgp.PGPException;
033    import org.bouncycastle.openpgp.PGPPrivateKey;
034    import org.bouncycastle.openpgp.PGPSecretKey;
035    import org.bouncycastle.openpgp.PGPSecretKeyRing;
036    import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
037    import org.bouncycastle.openpgp.PGPSignature;
038    import org.bouncycastle.openpgp.PGPSignatureGenerator;
039    import org.bouncycastle.openpgp.PGPUtil;
040    
041    /**
042     * Utils to do the signing with OpenPGP
043     * 
044     * @author Torsten Curdt <tcurdt@vafer.org>
045     */
046    
047    public final class SigningUtils {
048    
049        private static PGPSecretKey getSecretKey( final InputStream pInput, final String pKey ) throws IOException, PGPException {
050    
051            final PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(pInput));
052    
053            final Iterator rIt = pgpSec.getKeyRings();
054    
055            while (rIt.hasNext()) {
056                final PGPSecretKeyRing kRing = (PGPSecretKeyRing) rIt.next();
057                final Iterator kIt = kRing.getSecretKeys();
058    
059                while (kIt.hasNext()) {
060                    final PGPSecretKey k = (PGPSecretKey) kIt.next();
061    
062                    if (k.isSigningKey() && Long.toHexString(k.getKeyID() & 0xFFFFFFFFL).equals(pKey.toLowerCase())) {
063                        return k;
064                    }
065                }
066            }
067    
068            return null;
069        }
070    
071        /**
072         * Create a clear sign signature over the input data. (Not detached)
073         * 
074         * @param pInput
075         * @param pKeyring
076         * @param pKey
077         * @param pPassphrase
078         * @param pOutput
079         * @throws IOException
080         * @throws PGPException
081         * @throws NoSuchProviderException
082         * @throws NoSuchAlgorithmException
083         * @throws SignatureException
084         */
085        public static void clearSign( final InputStream pInput, final InputStream pKeyring, final String pKey, final String pPassphrase, final OutputStream pOutput ) throws IOException, PGPException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException {
086            
087            Security.addProvider( new BouncyCastleProvider() );
088            
089            final PGPSecretKey secretKey = getSecretKey(pKeyring, pKey);
090            final PGPPrivateKey privateKey = secretKey.extractPrivateKey(pPassphrase.toCharArray(), "BC");
091                    
092            final int digest = PGPUtil.SHA1;
093    
094            final PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), digest, "BC");
095            signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, privateKey);
096    
097    //        final PGPSignatureSubpacketGenerator subpackageGenerator = new PGPSignatureSubpacketGenerator();
098    //
099    //        final Iterator it = secretKey.getPublicKey().getUserIDs();
100    //        if (it.hasNext()) {
101    //            subpackageGenerator.setSignerUserID(false, (String)it.next());
102    //            signatureGenerator.setHashedSubpackets(subpackageGenerator.generate());
103    //        }
104        
105            final ArmoredOutputStream armoredOutput = new ArmoredOutputStream(pOutput);
106            
107            armoredOutput.beginClearText(digest);
108    
109            final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput));
110    
111            final byte[] newline = "\r\n".getBytes("UTF-8");
112            
113            processLine(reader.readLine(), armoredOutput, signatureGenerator);
114            
115            while(true) {
116                final String line = reader.readLine();
117                
118                if (line == null) {
119                    armoredOutput.write(newline);
120                    break;
121                }
122                            
123                armoredOutput.write(newline);
124                signatureGenerator.update(newline);
125    
126                processLine(line, armoredOutput, signatureGenerator);           
127            }
128            
129            armoredOutput.endClearText();
130            
131            final BCPGOutputStream pgpOutput = new BCPGOutputStream(armoredOutput);
132            
133            signatureGenerator.generate().encode(pgpOutput);
134    
135            armoredOutput.close();
136            
137        }
138    
139    
140        private static void processLine( final String pLine, final ArmoredOutputStream pArmoredOutput, final PGPSignatureGenerator pSignatureGenerator ) throws IOException, SignatureException {
141    
142            if (pLine == null) {
143                return;
144            }
145            
146            final char[] chars = pLine.toCharArray();
147            int len = chars.length;
148    
149            while(len > 0) {
150                if (!Character.isWhitespace(chars[len-1])) {
151                    break;
152                }
153                len--;
154            }
155    
156            final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
157            
158            pArmoredOutput.write(data);
159            pSignatureGenerator.update(data);
160        }
161    }