001    // Copyright 2012 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.internal.util;
016    
017    import org.apache.commons.codec.binary.Base64;
018    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
019    
020    import javax.crypto.Mac;
021    import java.io.IOException;
022    import java.io.OutputStream;
023    import java.security.Key;
024    
025    /**
026     * An output stream that wraps around a {@link Mac} (message authentication code algorithm).  This is currently
027     * used for symmetric (private) keys, but in theory could be used with assymetric (public/private) keys.
028     *
029     * @since 5.3.6
030     */
031    public class MacOutputStream extends OutputStream
032    {
033        private final Mac mac;
034    
035        public static MacOutputStream streamFor(Key key) throws IOException
036        {
037            try
038            {
039                Mac mac = Mac.getInstance(key.getAlgorithm());
040                mac.init(key);
041    
042                return new MacOutputStream(mac);
043            } catch (Exception ex)
044            {
045                throw new IOException("Unable to create MacOutputStream: " + InternalUtils.toMessage(ex), ex);
046            }
047        }
048    
049        public MacOutputStream(Mac mac)
050        {
051            assert mac != null;
052    
053            this.mac = mac;
054        }
055    
056        /**
057         * Should only be invoked once, immediately after this stream is closed; it generates the final MAC result, and
058         * returns it as a Base64 encoded string.
059         *
060         * @return Base64 encoded MAC result
061         */
062        public String getResult()
063        {
064            byte[] result = mac.doFinal();
065    
066            return new String(Base64.encodeBase64(result));
067        }
068    
069        @Override
070        public void write(int b) throws IOException
071        {
072            mac.update((byte) b);
073        }
074    
075        @Override
076        public void write(byte[] b) throws IOException
077        {
078            mac.update(b);
079        }
080    
081        @Override
082        public void write(byte[] b, int off, int len) throws IOException
083        {
084            mac.update(b, off, len);
085        }
086    }