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 }