001    /*
002     * Copyright 2005 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    }