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 }