    001// Copyright 2012-2013 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
    015package org.apache.tapestry5.internal.util;
    016
    017import org.apache.commons.codec.binary.Base64;
    018import org.apache.tapestry5.ioc.util.ExceptionUtils;
    019
    020import javax.crypto.Mac;
    021import java.io.IOException;
    022import java.io.OutputStream;
    023import 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 */
    031public 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: " + ExceptionUtils.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}