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 org.bouncycastle.bcpg.ArmoredOutputStream;
021 import org.bouncycastle.bcpg.BCPGOutputStream;
022 import org.bouncycastle.bcpg.HashAlgorithmTags;
023 import org.bouncycastle.jce.provider.BouncyCastleProvider;
024 import org.bouncycastle.openpgp.PGPException;
025 import org.bouncycastle.openpgp.PGPPrivateKey;
026 import org.bouncycastle.openpgp.PGPSecretKey;
027 import org.bouncycastle.openpgp.PGPSignature;
028 import org.bouncycastle.openpgp.PGPSignatureGenerator;
029 import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
030 import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
031 import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
032
033 import java.io.ByteArrayOutputStream;
034 import java.io.IOException;
035 import java.io.OutputStream;
036 import java.security.Security;
037 import java.security.SignatureException;
038
039 /**
040 * Bouncy Castle implementation of the OpenPGP signer.
041 *
042 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
043 */
044 public class BouncyCastleOpenPgpStreamingSigner
045 implements OpenPgpStreamingSigner
046 {
047 private static final String PROVIDER = "BC";
048
049 private PGPSignatureGenerator sGen;
050
051 private final ByteArrayOutputStream signatureBytes;
052
053 private BCPGOutputStream bOut;
054
055 public BouncyCastleOpenPgpStreamingSigner( String keyId, KeyRing keyRing, boolean asciiArmor )
056 throws OpenPgpException
057 {
058 signatureBytes = new ByteArrayOutputStream();
059 init( asciiArmor, signatureBytes, keyRing, keyId );
060 }
061
062 public BouncyCastleOpenPgpStreamingSigner( OutputStream signature, String keyId, KeyRing keyRing,
063 boolean asciiArmor )
064 throws OpenPgpException
065 {
066 signatureBytes = null;
067 init( asciiArmor, signature, keyRing, keyId );
068 }
069
070 private void init( boolean asciiArmor, OutputStream signature, KeyRing keyRing, String keyId )
071 throws OpenPgpException
072 {
073 // TODO: better location for this?
074 Security.addProvider( new BouncyCastleProvider() );
075
076 OutputStream out;
077 if ( asciiArmor )
078 {
079 out = new ArmoredOutputStream( signature );
080 }
081 else
082 {
083 out = signature;
084 }
085 bOut = new BCPGOutputStream( out );
086
087 try
088 {
089 PGPSecretKey pgpSec = keyRing.getSecretKey( keyId );
090 if ( pgpSec == null )
091 {
092 throw new OpenPgpException( "The key with id '" + keyId + "' was not found in the secret keyring." );
093 }
094 PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(
095 new JcaPGPDigestCalculatorProviderBuilder().setProvider(PROVIDER).build()).setProvider(PROVIDER).
096 build(keyRing.getPassword()));
097 sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(),
098 HashAlgorithmTags.SHA1).setProvider(PROVIDER).setDigestProvider(PROVIDER));
099 sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
100 }
101 catch ( PGPException e )
102 {
103 // TODO: more details
104 throw new OpenPgpException( "Error calculating detached signature", e );
105 }
106 }
107
108 public void update( byte[] buf )
109 throws OpenPgpException
110 {
111 update( buf, 0, buf.length );
112 }
113
114 public void update( byte[] buf, int offset, int length )
115 throws OpenPgpException
116 {
117 try
118 {
119 sGen.update( buf, offset, length );
120 }
121 catch ( SignatureException e )
122 {
123 // TODO: more details
124 throw new OpenPgpException( "Error calculating detached signature", e );
125 }
126 }
127
128 public byte[] finish()
129 throws OpenPgpException, IOException
130 {
131 try
132 {
133 sGen.generate().encode( bOut );
134 }
135 catch ( PGPException e )
136 {
137 // TODO: more details
138 throw new OpenPgpException( "Error calculating detached signature", e );
139 }
140 catch ( SignatureException e )
141 {
142 // TODO: more details
143 throw new OpenPgpException( "Error calculating detached signature", e );
144 }
145 bOut.close();
146 return signatureBytes != null ? signatureBytes.toByteArray() : null;
147 }
148 }