proguard.io.DEROutputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-core Show documentation
Show all versions of proguard-core Show documentation
ProGuardCORE is a free library to read, analyze, modify, and write Java class files.
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2020 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.io;
import java.io.*;
import java.math.BigInteger;
import java.util.Stack;
/**
* This class provides methods to write data with Distinguished Encoding Rules
* to an underlying output stream. DER is an encoding of ASN.1 (Abstract
* Syntax Notation).
*
* This is for example the format of signature files like META-INF/CERT.RSA
*
* You can view the resulting file with
* openssl asn1parse -inform DER META-INF/CERT.RSA
*
* @author Eric Lafortune
*/
class DEROutputStream // extends OutputStream
{
private static final byte BOOLEAN_TAG = 0x01;
private static final byte INTEGER_TAG = 0x02;
private static final byte BIT_STRING_TAG = 0x03;
private static final byte OCTET_STRING_TAG = 0x04;
private static final byte NULL_TAG = 0x05;
private static final byte OBJECT_IDENTIFIER_TAG = 0x06;
private static final byte UTF8_STRING_TAG = 0x0c;
private static final byte PRINTABLE_STRING_TAG = 0x13;
private static final byte T61_STRING_TAG = 0x14;
private static final byte IA5_STRING_TAG = 0x16;
private static final byte UTC_TIME_TAG = 0x17;
private static final byte SEQUENCE_TAG = 0x30;
private static final byte SET_TAG = 0x31;
private static final byte IMPLICIT_TAG = (byte)0xa0;
private OutputStream outputStream;
// A stack to push output streams when we're starting sequences, sets,
// and implicits.
private final Stack outputStreams = new Stack();
/**
* Creates a new DEROutputStream.
* @param outputStream the delegate output stream.
*/
public DEROutputStream(OutputStream outputStream)
{
this.outputStream = outputStream;
}
/**
* Writes the given integer, compressed in 2 to 5 bytes.
*/
public void writeInteger(int i) throws IOException
{
outputStream.write(INTEGER_TAG);
if (i << 24 >>> 24 == i)
{
outputStream.write(1);
outputStream.write(i);
}
else if (i << 16 >>> 16 == i)
{
outputStream.write(2);
outputStream.write(i >> 8);
outputStream.write(i );
}
else if (i << 8 >>> 8 == i)
{
outputStream.write(3);
outputStream.write(i >> 16);
outputStream.write(i >> 8);
outputStream.write(i );
}
else
{
outputStream.write(4);
outputStream.write(i >> 24);
outputStream.write(i >> 16);
outputStream.write(i >> 8);
outputStream.write(i );
}
}
/**
* Writes the given big integer.
*/
public void writeInteger(BigInteger bigInteger) throws IOException
{
byte[] bytes = bigInteger.toByteArray();
write(INTEGER_TAG, bytes);
}
/**
* Writes the given byte array.
*/
public void writeOctetString(byte[] bytes) throws IOException
{
write(OCTET_STRING_TAG, bytes);
}
/**
* Writes a null element.
*/
public void writeNull() throws IOException
{
outputStream.write(NULL_TAG);
outputStream.write(0);
}
/**
* Writes the given object identifier.
*/
public void writeObjectIdentifier(byte[] bytes) throws IOException
{
write(OBJECT_IDENTIFIER_TAG, bytes);
}
/**
* Writes the given string as a printable string, as a UTF-8 string
* (if necessary), or as an IA5 string (if specified).
*/
public void writeString(String s, boolean asIA5String) throws IOException
{
if (asIA5String)
{
writeIA5String(s);
}
else if (isPrintable(s))
{
writePrintableString(s);
}
else
{
writeUtf8String(s);
}
}
/**
* Writes the given string in UTF-8 encoding.
*/
public void writeUtf8String(String s) throws IOException
{
byte[] bytes = s.getBytes("UTF8");
write(UTF8_STRING_TAG, bytes);
}
/**
* Writes the given string in T61 encoding.
*/
public void writeT61String(String s) throws IOException
{
byte[] bytes = s.getBytes("ASCII");
write(T61_STRING_TAG, bytes);
}
/**
* Writes the given string in IA5 encoding.
*/
public void writeIA5String(String s) throws IOException
{
byte[] bytes = s.getBytes("ASCII");
write(IA5_STRING_TAG, bytes);
}
/**
* Writes the given string in printable encoding.
*/
public void writePrintableString(String s) throws IOException
{
byte[] bytes = s.getBytes("ASCII");
write(PRINTABLE_STRING_TAG, bytes);
}
/**
* Writes the given integer as a length, compressed in 1 to 5 bytes.
*/
private void writeLength(int i) throws IOException
{
if (i <= 0x0000007f)
{
outputStream.write(i);
}
else if (i <= 0x000000ff)
{
outputStream.write(0x81);
outputStream.write(i);
}
else if (i <= 0x0000ffff)
{
outputStream.write(0x82);
outputStream.write(i >> 8);
outputStream.write(i );
}
else if (i <= 0x00ffffff)
{
outputStream.write(0x83);
outputStream.write(i >> 16);
outputStream.write(i >> 8);
outputStream.write(i );
}
else
{
outputStream.write(0x84);
outputStream.write(i >> 24);
outputStream.write(i >> 16);
outputStream.write(i >> 8);
outputStream.write(i );
}
}
/**
* Starts a sequence.
*/
public void startSequence() throws IOException
{
pushOutputStream();
}
/**
* Ends the most recently started sequence and writes it out.
*/
public void endSequence() throws IOException
{
byte[] bytes = popOutputStream();
writeSequence(bytes);
}
/**
* Writes the given byte array representation of a sequence.
*/
private void writeSequence(byte[] bytes) throws IOException
{
write(SEQUENCE_TAG, bytes);
}
/**
* Starts a set.
*/
public void startSet() throws IOException
{
pushOutputStream();
}
/**
* Ends the most recently started set and writes it out.
*/
public void endSet() throws IOException
{
byte[] bytes = popOutputStream();
writeSet(bytes);
}
/**
* Writes the given byte array representation of a set.
*/
private void writeSet(byte[] bytes) throws IOException
{
write(SET_TAG, bytes);
}
/**
* Starts an implicit.
*/
public void startImplicit() throws IOException
{
pushOutputStream();
}
/**
* Ends the most recently started implicit and writes it out.
*/
public void endImplicit() throws IOException
{
byte[] bytes = popOutputStream();
writeImplicit(bytes);
}
/**
* Writes the given byte array representation of an implicit.
*/
public void writeImplicit(byte[] bytes) throws IOException
{
write(IMPLICIT_TAG, bytes);
}
/**
* Writes the given tag and byte array, with the proper length.
*/
public void write(byte tag, byte[] bytes) throws IOException
{
outputStream.write(tag);
writeLength(bytes.length);
outputStream.write(bytes);
}
// Implementations for OutputStream.
//public void write(int b) throws IOException
//{
// outputStream.write(b);
//}
//
//
//public void write(byte[] bytes) throws IOException
//{
// write(bytes, 0, bytes.length);
//}
//
//
//public void write(byte[] bytes, int offset, int size) throws IOException
//{
// outputStream.write(bytes, offset, size);
//}
/**
* Flushes the output stream.
*/
public void flush() throws IOException
{
outputStream.flush();
}
/**
* Closes the output stream.
*/
public void close() throws IOException
{
if (!outputStreams.isEmpty())
{
throw new IllegalStateException("DER sequences/sets not ended properly");
}
outputStream.close();
}
// Small utility methods.
/**
* Returns whether the given string can be represented as a printable
* string.
*/
private boolean isPrintable(String s)
{
for (int index = 0; index < s.length(); index++)
{
char c = s.charAt(index);
if (c < 32 || c >= 128)
{
return false;
}
}
return true;
}
/**
* Pushes the current output stream, replacing it by a byte array output
* stream to temporarily collect output.
*/
private void pushOutputStream()
{
outputStreams.push(outputStream);
outputStream = new ByteArrayOutputStream();
}
/**
* Returns the bytes from the current byte array output stream
* and restores the previous output stream.
*/
private byte[] popOutputStream()
{
byte[] bytes = ((ByteArrayOutputStream)outputStream).toByteArray();
outputStream = (OutputStream)outputStreams.pop();
return bytes;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy