update to android master on May.30.2016, not tested

pull/6/head
cfig 9 years ago
parent 395d66ef9e
commit 56dacf31bd

@ -149,6 +149,7 @@ public class BootSignature extends ASN1Object
throws Exception, IOException, CertificateEncodingException {
ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
certificate = s.readObject();
publicKey = cert.getPublicKey();
}
public byte[] generateSignableImage(byte[] image) throws IOException {
@ -253,7 +254,7 @@ public class BootSignature extends ASN1Object
Utils.write(image_with_metadata, outPath);
}
public static void verifySignature(String imagePath) throws Exception {
public static void verifySignature(String imagePath, String certPath) throws Exception {
byte[] image = Utils.read(imagePath);
int signableSize = getSignableImageSize(image);
@ -264,6 +265,11 @@ public class BootSignature extends ASN1Object
byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
BootSignature bootsig = new BootSignature(signature);
if (!certPath.isEmpty()) {
System.err.println("NOTE: verifying using public key from " + certPath);
bootsig.setCertificate(Utils.loadPEMCertificate(certPath));
}
try {
if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
System.err.println("Signature is VALID");
@ -291,8 +297,15 @@ public class BootSignature extends ASN1Object
Security.addProvider(new BouncyCastleProvider());
if ("-verify".equals(args[0])) {
String certPath = "";
if (args.length >= 4 && "-certificate".equals(args[2])) {
/* args[3] is the path to a public key certificate */
certPath = args[3];
}
/* args[1] is the path to a signed boot image */
verifySignature(args[1]);
verifySignature(args[1], certPath);
} else {
/* args[0] is the target name, typically /boot
args[1] is the path to a boot image to sign

@ -258,10 +258,10 @@ public class Utils {
static boolean verify(PublicKey key, byte[] input, byte[] signature,
AlgorithmIdentifier algId) throws Exception {
String algName = ID_TO_ALG.get(algId.getObjectId().getId());
String algName = ID_TO_ALG.get(algId.getAlgorithm().getId());
if (algName == null) {
throw new IllegalArgumentException("Unsupported algorithm " + algId.getObjectId());
throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm());
}
Signature verifier = Signature.getInstance(algName);

@ -53,20 +53,36 @@ public class AttributeCertificateHolder
holder = Holder.getInstance(seq);
}
/**
* Create a holder using the baseCertificateID element.
*
* @param issuerName name of associated certificate's issuer.
* @param serialNumber serial number of associated certificate.
*/
public AttributeCertificateHolder(X500Name issuerName,
BigInteger serialNumber)
{
holder = new Holder(new IssuerSerial(
new GeneralNames(new GeneralName(issuerName)),
generateGeneralNames(issuerName),
new ASN1Integer(serialNumber)));
}
/**
* Create a holder using the baseCertificateID option based on the passed in associated certificate,
*
* @param cert the certificate to be associated with this holder.
*/
public AttributeCertificateHolder(X509CertificateHolder cert)
{
holder = new Holder(new IssuerSerial(generateGeneralNames(cert.getIssuer()),
new ASN1Integer(cert.getSerialNumber())));
}
/**
* Create a holder using the entityName option based on the passed in principal.
*
* @param principal the entityName to be associated with the attribute certificate.
*/
public AttributeCertificateHolder(X500Name principal)
{
holder = new Holder(generateGeneralNames(principal));

@ -21,11 +21,13 @@ import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.util.Encodable;
/**
* Holding class for an X.509 AttributeCertificate structure.
*/
public class X509AttributeCertificateHolder
implements Encodable
{
private static Attribute[] EMPTY_ARRAY = new Attribute[0];
@ -277,7 +279,7 @@ public class X509AttributeCertificateHolder
*/
public byte[] getSignature()
{
return attrCert.getSignatureValue().getBytes();
return attrCert.getSignatureValue().getOctets();
}
/**
@ -338,7 +340,7 @@ public class X509AttributeCertificateHolder
throw new CertException("unable to process signature: " + e.getMessage(), e);
}
return verifier.verify(attrCert.getSignatureValue().getBytes());
return verifier.verify(this.getSignature());
}
public boolean equals(

@ -24,11 +24,13 @@ import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
import org.bouncycastle.asn1.x509.TBSCertList;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.util.Encodable;
/**
* Holding class for an X.509 CRL structure.
*/
public class X509CRLHolder
implements Encodable
{
private CertificateList x509CRL;
private boolean isIndirect;
@ -289,7 +291,7 @@ public class X509CRLHolder
throw new CertException("unable to process signature: " + e.getMessage(), e);
}
return verifier.verify(x509CRL.getSignature().getBytes());
return verifier.verify(x509CRL.getSignature().getOctets());
}
public boolean equals(

@ -19,11 +19,13 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.util.Encodable;
/**
* Holding class for an X.509 Certificate structure.
*/
public class X509CertificateHolder
implements Encodable
{
private Certificate x509Certificate;
private Extensions extensions;
@ -214,7 +216,7 @@ public class X509CertificateHolder
/**
* Return the underlying ASN.1 structure for the certificate in this holder.
*
* @return a X509CertificateStructure object.
* @return a Certificate object.
*/
public Certificate toASN1Structure()
{
@ -238,7 +240,7 @@ public class X509CertificateHolder
*/
public byte[] getSignature()
{
return x509Certificate.getSignature().getBytes();
return x509Certificate.getSignature().getOctets();
}
/**
@ -287,7 +289,7 @@ public class X509CertificateHolder
throw new CertException("unable to process signature: " + e.getMessage(), e);
}
return verifier.verify(x509Certificate.getSignature().getBytes());
return verifier.verify(this.getSignature());
}
public boolean equals(

@ -0,0 +1,225 @@
package org.bouncycastle.cert.ocsp;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
import org.bouncycastle.asn1.ocsp.ResponseData;
import org.bouncycastle.asn1.ocsp.SingleResponse;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.util.Encodable;
/**
* <pre>
* BasicOCSPResponse ::= SEQUENCE {
* tbsResponseData ResponseData,
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING,
* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
* </pre>
*/
public class BasicOCSPResp
implements Encodable
{
private BasicOCSPResponse resp;
private ResponseData data;
private Extensions extensions;
public BasicOCSPResp(
BasicOCSPResponse resp)
{
this.resp = resp;
this.data = resp.getTbsResponseData();
this.extensions = Extensions.getInstance(resp.getTbsResponseData().getResponseExtensions());
}
/**
* Return the DER encoding of the tbsResponseData field.
* @return DER encoding of tbsResponseData
*/
public byte[] getTBSResponseData()
{
try
{
return resp.getTbsResponseData().getEncoded(ASN1Encoding.DER);
}
catch (IOException e)
{
return null;
}
}
/**
* Return the algorithm identifier describing the signature used in the response.
*
* @return an AlgorithmIdentifier
*/
public AlgorithmIdentifier getSignatureAlgorithmID()
{
return resp.getSignatureAlgorithm();
}
public int getVersion()
{
return data.getVersion().getValue().intValue() + 1;
}
public RespID getResponderId()
{
return new RespID(data.getResponderID());
}
public Date getProducedAt()
{
return OCSPUtils.extractDate(data.getProducedAt());
}
public SingleResp[] getResponses()
{
ASN1Sequence s = data.getResponses();
SingleResp[] rs = new SingleResp[s.size()];
for (int i = 0; i != rs.length; i++)
{
rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i)));
}
return rs;
}
public boolean hasExtensions()
{
return extensions != null;
}
public Extension getExtension(ASN1ObjectIdentifier oid)
{
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
public List getExtensionOIDs()
{
return OCSPUtils.getExtensionOIDs(extensions);
}
public Set getCriticalExtensionOIDs()
{
return OCSPUtils.getCriticalExtensionOIDs(extensions);
}
public Set getNonCriticalExtensionOIDs()
{
return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
}
public ASN1ObjectIdentifier getSignatureAlgOID()
{
return resp.getSignatureAlgorithm().getAlgorithm();
}
public byte[] getSignature()
{
return resp.getSignature().getOctets();
}
public X509CertificateHolder[] getCerts()
{
//
// load the certificates if we have any
//
if (resp.getCerts() != null)
{
ASN1Sequence s = resp.getCerts();
if (s != null)
{
X509CertificateHolder[] certs = new X509CertificateHolder[s.size()];
for (int i = 0; i != certs.length; i++)
{
certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i)));
}
return certs;
}
return OCSPUtils.EMPTY_CERTS;
}
else
{
return OCSPUtils.EMPTY_CERTS;
}
}
/**
* verify the signature against the tbsResponseData object we contain.
*/
public boolean isSignatureValid(
ContentVerifierProvider verifierProvider)
throws OCSPException
{
try
{
ContentVerifier verifier = verifierProvider.get(resp.getSignatureAlgorithm());
OutputStream vOut = verifier.getOutputStream();
vOut.write(resp.getTbsResponseData().getEncoded(ASN1Encoding.DER));
vOut.close();
return verifier.verify(this.getSignature());
}
catch (Exception e)
{
throw new OCSPException("exception processing sig: " + e, e);
}
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] getEncoded()
throws IOException
{
return resp.getEncoded();
}
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof BasicOCSPResp))
{
return false;
}
BasicOCSPResp r = (BasicOCSPResp)o;
return resp.equals(r.resp);
}
public int hashCode()
{
return resp.hashCode();
}
}

@ -0,0 +1,283 @@
package org.bouncycastle.cert.ocsp;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERGeneralizedTime;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
import org.bouncycastle.asn1.ocsp.CertStatus;
import org.bouncycastle.asn1.ocsp.ResponseData;
import org.bouncycastle.asn1.ocsp.RevokedInfo;
import org.bouncycastle.asn1.ocsp.SingleResponse;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculator;
/**
* Generator for basic OCSP response objects.
*/
public class BasicOCSPRespBuilder
{
private List list = new ArrayList();
private Extensions responseExtensions = null;
private RespID responderID;
private class ResponseObject
{
CertificateID certId;
CertStatus certStatus;
ASN1GeneralizedTime thisUpdate;
ASN1GeneralizedTime nextUpdate;
Extensions extensions;
public ResponseObject(
CertificateID certId,
CertificateStatus certStatus,
Date thisUpdate,
Date nextUpdate,
Extensions extensions)
{
this.certId = certId;
if (certStatus == null)
{
this.certStatus = new CertStatus();
}
else if (certStatus instanceof UnknownStatus)
{
this.certStatus = new CertStatus(2, DERNull.INSTANCE);
}
else
{
RevokedStatus rs = (RevokedStatus)certStatus;
if (rs.hasRevocationReason())
{
this.certStatus = new CertStatus(
new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), CRLReason.lookup(rs.getRevocationReason())));
}
else
{
this.certStatus = new CertStatus(
new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), null));
}
}
this.thisUpdate = new DERGeneralizedTime(thisUpdate);
if (nextUpdate != null)
{
this.nextUpdate = new DERGeneralizedTime(nextUpdate);
}
else
{
this.nextUpdate = null;
}
this.extensions = extensions;
}
public SingleResponse toResponse()
throws Exception
{
return new SingleResponse(certId.toASN1Primitive(), certStatus, thisUpdate, nextUpdate, extensions);
}
}
/**
* basic constructor
*/
public BasicOCSPRespBuilder(
RespID responderID)
{
this.responderID = responderID;
}
/**
* construct with the responderID to be the SHA-1 keyHash of the passed in public key.
*
* @param key the key info of the responder public key.
* @param digCalc a SHA-1 digest calculator
*/
public BasicOCSPRespBuilder(
SubjectPublicKeyInfo key,
DigestCalculator digCalc)
throws OCSPException
{
this.responderID = new RespID(key, digCalc);
}
/**
* Add a response for a particular Certificate ID.
*
* @param certID certificate ID details
* @param certStatus status of the certificate - null if okay
*/
public BasicOCSPRespBuilder addResponse(
CertificateID certID,
CertificateStatus certStatus)
{
this.addResponse(certID, certStatus, new Date(), null, null);
return this;
}
/**
* Add a response for a particular Certificate ID.
*
* @param certID certificate ID details
* @param certStatus status of the certificate - null if okay
* @param singleExtensions optional extensions
*/
public BasicOCSPRespBuilder addResponse(
CertificateID certID,
CertificateStatus certStatus,
Extensions singleExtensions)
{
this.addResponse(certID, certStatus, new Date(), null, singleExtensions);
return this;
}
/**
* Add a response for a particular Certificate ID.
*
* @param certID certificate ID details
* @param nextUpdate date when next update should be requested
* @param certStatus status of the certificate - null if okay
* @param singleExtensions optional extensions
*/
public BasicOCSPRespBuilder addResponse(
CertificateID certID,
CertificateStatus certStatus,
Date nextUpdate,
Extensions singleExtensions)
{
this.addResponse(certID, certStatus, new Date(), nextUpdate, singleExtensions);
return this;
}
/**
* Add a response for a particular Certificate ID.
*
* @param certID certificate ID details
* @param thisUpdate date this response was valid on
* @param nextUpdate date when next update should be requested
* @param certStatus status of the certificate - null if okay
*/
public BasicOCSPRespBuilder addResponse(
CertificateID certID,
CertificateStatus certStatus,
Date thisUpdate,
Date nextUpdate)
{
this.addResponse(certID, certStatus, thisUpdate, nextUpdate, null);
return this;
}
/**
* Add a response for a particular Certificate ID.
*
* @param certID certificate ID details
* @param thisUpdate date this response was valid on
* @param nextUpdate date when next update should be requested
* @param certStatus status of the certificate - null if okay
* @param singleExtensions optional extensions
*/
public BasicOCSPRespBuilder addResponse(
CertificateID certID,
CertificateStatus certStatus,
Date thisUpdate,
Date nextUpdate,
Extensions singleExtensions)
{
list.add(new ResponseObject(certID, certStatus, thisUpdate, nextUpdate, singleExtensions));
return this;
}
/**
* Set the extensions for the response.
*
* @param responseExtensions the extension object to carry.
*/
public BasicOCSPRespBuilder setResponseExtensions(
Extensions responseExtensions)
{
this.responseExtensions = responseExtensions;
return this;
}
public BasicOCSPResp build(
ContentSigner signer,
X509CertificateHolder[] chain,
Date producedAt)
throws OCSPException
{
Iterator it = list.iterator();
ASN1EncodableVector responses = new ASN1EncodableVector();
while (it.hasNext())
{
try
{
responses.add(((ResponseObject)it.next()).toResponse());
}
catch (Exception e)
{
throw new OCSPException("exception creating Request", e);
}
}
ResponseData tbsResp = new ResponseData(responderID.toASN1Primitive(), new ASN1GeneralizedTime(producedAt), new DERSequence(responses), responseExtensions);
DERBitString bitSig;
try
{
OutputStream sigOut = signer.getOutputStream();
sigOut.write(tbsResp.getEncoded(ASN1Encoding.DER));
sigOut.close();
bitSig = new DERBitString(signer.getSignature());
}
catch (Exception e)
{
throw new OCSPException("exception processing TBSRequest: " + e.getMessage(), e);
}
AlgorithmIdentifier sigAlgId = signer.getAlgorithmIdentifier();
DERSequence chainSeq = null;
if (chain != null && chain.length > 0)
{
ASN1EncodableVector v = new ASN1EncodableVector();
for (int i = 0; i != chain.length; i++)
{
v.add(chain[i].toASN1Structure());
}
chainSeq = new DERSequence(v);
}
return new BasicOCSPResp(new BasicOCSPResponse(tbsResp, sigAlgId, bitSig, chainSeq));
}
}

@ -0,0 +1,156 @@
package org.bouncycastle.cert.ocsp;
import java.io.OutputStream;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.CertID;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
public class CertificateID
{
public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
private final CertID id;
public CertificateID(
CertID id)
{
if (id == null)
{
throw new IllegalArgumentException("'id' cannot be null");
}
this.id = id;
}
/**
* create from an issuer certificate and the serial number of the
* certificate it signed.
*
* @param issuerCert issuing certificate
* @param number serial number
*
* @exception OCSPException if any problems occur creating the id fields.
*/
public CertificateID(
DigestCalculator digestCalculator, X509CertificateHolder issuerCert,
BigInteger number)
throws OCSPException
{
this.id = createCertID(digestCalculator, issuerCert, new ASN1Integer(number));
}
public ASN1ObjectIdentifier getHashAlgOID()
{
return id.getHashAlgorithm().getAlgorithm();
}
public byte[] getIssuerNameHash()
{
return id.getIssuerNameHash().getOctets();
}
public byte[] getIssuerKeyHash()
{
return id.getIssuerKeyHash().getOctets();
}
/**
* return the serial number for the certificate associated
* with this request.
*/
public BigInteger getSerialNumber()
{
return id.getSerialNumber().getValue();
}
public boolean matchesIssuer(X509CertificateHolder issuerCert, DigestCalculatorProvider digCalcProvider)
throws OCSPException
{
try
{
return createCertID(digCalcProvider.get(id.getHashAlgorithm()), issuerCert, id.getSerialNumber()).equals(id);
}
catch (OperatorCreationException e)
{
throw new OCSPException("unable to create digest calculator: " + e.getMessage(), e);
}
}
public CertID toASN1Primitive()
{
return id;
}
public boolean equals(
Object o)
{
if (!(o instanceof CertificateID))
{
return false;
}
CertificateID obj = (CertificateID)o;
return id.toASN1Primitive().equals(obj.id.toASN1Primitive());
}
public int hashCode()
{
return id.toASN1Primitive().hashCode();
}
/**
* Create a new CertificateID for a new serial number derived from a previous one
* calculated for the same CA certificate.
*
* @param original the previously calculated CertificateID for the CA.
* @param newSerialNumber the serial number for the new certificate of interest.
*
* @return a new CertificateID for newSerialNumber
*/
public static CertificateID deriveCertificateID(CertificateID original, BigInteger newSerialNumber)
{
return new CertificateID(new CertID(original.id.getHashAlgorithm(), original.id.getIssuerNameHash(), original.id.getIssuerKeyHash(), new ASN1Integer(newSerialNumber)));
}
private static CertID createCertID(DigestCalculator digCalc, X509CertificateHolder issuerCert, ASN1Integer serialNumber)
throws OCSPException
{
try
{
OutputStream dgOut = digCalc.getOutputStream();
dgOut.write(issuerCert.toASN1Structure().getSubject().getEncoded(ASN1Encoding.DER));
dgOut.close();
ASN1OctetString issuerNameHash = new DEROctetString(digCalc.getDigest());
SubjectPublicKeyInfo info = issuerCert.getSubjectPublicKeyInfo();
dgOut = digCalc.getOutputStream();
dgOut.write(info.getPublicKeyData().getBytes());
dgOut.close();
ASN1OctetString issuerKeyHash = new DEROctetString(digCalc.getDigest());
return new CertID(digCalc.getAlgorithmIdentifier(), issuerNameHash, issuerKeyHash, serialNumber);
}
catch (Exception e)
{
throw new OCSPException("problem creating ID: " + e, e);
}
}
}

@ -0,0 +1,6 @@
package org.bouncycastle.cert.ocsp;
public interface CertificateStatus
{
public static final CertificateStatus GOOD = null;
}

@ -0,0 +1,27 @@
package org.bouncycastle.cert.ocsp;
public class OCSPException
extends Exception
{
private Throwable cause;
public OCSPException(
String name)
{
super(name);
}
public OCSPException(
String name,
Throwable cause)
{
super(name);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,259 @@
package org.bouncycastle.cert.ocsp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Exception;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ocsp.OCSPRequest;
import org.bouncycastle.asn1.ocsp.Request;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
/**
* <pre>
* OCSPRequest ::= SEQUENCE {
* tbsRequest TBSRequest,
* optionalSignature [0] EXPLICIT Signature OPTIONAL }
*
* TBSRequest ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* requestorName [1] EXPLICIT GeneralName OPTIONAL,
* requestList SEQUENCE OF Request,
* requestExtensions [2] EXPLICIT Extensions OPTIONAL }
*
* Signature ::= SEQUENCE {
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING,
* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
*
* Version ::= INTEGER { v1(0) }
*
* Request ::= SEQUENCE {
* reqCert CertID,
* singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
*
* CertID ::= SEQUENCE {
* hashAlgorithm AlgorithmIdentifier,
* issuerNameHash OCTET STRING, -- Hash of Issuer's DN
* issuerKeyHash OCTET STRING, -- Hash of Issuers public key
* serialNumber CertificateSerialNumber }
* </pre>
*/
public class OCSPReq
{
private static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0];
private OCSPRequest req;
private Extensions extensions;
public OCSPReq(
OCSPRequest req)
{
this.req = req;
this.extensions = req.getTbsRequest().getRequestExtensions();
}
public OCSPReq(
byte[] req)
throws IOException
{
this(new ASN1InputStream(req));
}
private OCSPReq(
ASN1InputStream aIn)
throws IOException
{
try
{
this.req = OCSPRequest.getInstance(aIn.readObject());
if (req == null)
{
throw new CertIOException("malformed request: no request data found");
}
this.extensions = req.getTbsRequest().getRequestExtensions();
}
catch (IllegalArgumentException e)
{
throw new CertIOException("malformed request: " + e.getMessage(), e);
}
catch (ClassCastException e)
{
throw new CertIOException("malformed request: " + e.getMessage(), e);
}
catch (ASN1Exception e)
{
throw new CertIOException("malformed request: " + e.getMessage(), e);
}
}
public int getVersionNumber()
{
return req.getTbsRequest().getVersion().getValue().intValue() + 1;
}
public GeneralName getRequestorName()
{
return GeneralName.getInstance(req.getTbsRequest().getRequestorName());
}
public Req[] getRequestList()
{
ASN1Sequence seq = req.getTbsRequest().getRequestList();
Req[] requests = new Req[seq.size()];
for (int i = 0; i != requests.length; i++)
{
requests[i] = new Req(Request.getInstance(seq.getObjectAt(i)));
}
return requests;
}
public boolean hasExtensions()
{
return extensions != null;
}
public Extension getExtension(ASN1ObjectIdentifier oid)
{
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
public List getExtensionOIDs()
{
return OCSPUtils.getExtensionOIDs(extensions);
}
public Set getCriticalExtensionOIDs()
{
return OCSPUtils.getCriticalExtensionOIDs(extensions);
}
public Set getNonCriticalExtensionOIDs()
{
return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
}
/**
* return the object identifier representing the signature algorithm
*/
public ASN1ObjectIdentifier getSignatureAlgOID()
{
if (!this.isSigned())
{
return null;
}
return req.getOptionalSignature().getSignatureAlgorithm().getAlgorithm();
}
public byte[] getSignature()
{
if (!this.isSigned())
{
return null;
}
return req.getOptionalSignature().getSignature().getOctets();
}
public X509CertificateHolder[] getCerts()
{
//
// load the certificates if we have any
//
if (req.getOptionalSignature() != null)
{
ASN1Sequence s = req.getOptionalSignature().getCerts();
if (s != null)
{
X509CertificateHolder[] certs = new X509CertificateHolder[s.size()];
for (int i = 0; i != certs.length; i++)
{
certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i)));
}
return certs;
}
return EMPTY_CERTS;
}
else
{
return EMPTY_CERTS;
}
}
/**
* Return whether or not this request is signed.
*
* @return true if signed false otherwise.
*/
public boolean isSigned()
{
return req.getOptionalSignature() != null;
}
/**
* verify the signature against the TBSRequest object we contain.
*/
public boolean isSignatureValid(
ContentVerifierProvider verifierProvider)
throws OCSPException
{
if (!this.isSigned())
{
throw new OCSPException("attempt to verify signature on unsigned object");
}
try
{
ContentVerifier verifier = verifierProvider.get(req.getOptionalSignature().getSignatureAlgorithm());
OutputStream sOut = verifier.getOutputStream();
sOut.write(req.getTbsRequest().getEncoded(ASN1Encoding.DER));
return verifier.verify(this.getSignature());
}
catch (Exception e)
{
throw new OCSPException("exception processing signature: " + e, e);
}
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] getEncoded()
throws IOException
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
aOut.writeObject(req);
return bOut.toByteArray();
}
}

@ -0,0 +1,199 @@
package org.bouncycastle.cert.ocsp;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.ocsp.OCSPRequest;
import org.bouncycastle.asn1.ocsp.Request;
import org.bouncycastle.asn1.ocsp.Signature;
import org.bouncycastle.asn1.ocsp.TBSRequest;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentSigner;
public class OCSPReqBuilder
{
private List list = new ArrayList();
private GeneralName requestorName = null;
private Extensions requestExtensions = null;
private class RequestObject
{
CertificateID certId;
Extensions extensions;
public RequestObject(
CertificateID certId,
Extensions extensions)
{
this.certId = certId;
this.extensions = extensions;
}
public Request toRequest()
throws Exception
{
return new Request(certId.toASN1Primitive(), extensions);
}
}
/**
* Add a request for the given CertificateID.
*
* @param certId certificate ID of interest
*/
public OCSPReqBuilder addRequest(
CertificateID certId)
{
list.add(new RequestObject(certId, null));
return this;
}
/**
* Add a request with extensions
*
* @param certId certificate ID of interest
* @param singleRequestExtensions the extensions to attach to the request
*/
public OCSPReqBuilder addRequest(
CertificateID certId,
Extensions singleRequestExtensions)
{
list.add(new RequestObject(certId, singleRequestExtensions));
return this;
}
/**
* Set the requestor name to the passed in X500Principal
*
* @param requestorName a X500Principal representing the requestor name.
*/
public OCSPReqBuilder setRequestorName(
X500Name requestorName)
{
this.requestorName = new GeneralName(GeneralName.directoryName, requestorName);
return this;
}
public OCSPReqBuilder setRequestorName(
GeneralName requestorName)
{
this.requestorName = requestorName;
return this;
}
public OCSPReqBuilder setRequestExtensions(
Extensions requestExtensions)
{
this.requestExtensions = requestExtensions;
return this;
}
private OCSPReq generateRequest(
ContentSigner contentSigner,
X509CertificateHolder[] chain)
throws OCSPException
{
Iterator it = list.iterator();
ASN1EncodableVector requests = new ASN1EncodableVector();
while (it.hasNext())
{
try
{
requests.add(((RequestObject)it.next()).toRequest());
}
catch (Exception e)
{
throw new OCSPException("exception creating Request", e);
}
}
TBSRequest tbsReq = new TBSRequest(requestorName, new DERSequence(requests), requestExtensions);
Signature signature = null;
if (contentSigner != null)
{
if (requestorName == null)
{
throw new OCSPException("requestorName must be specified if request is signed.");
}
try
{
OutputStream sOut = contentSigner.getOutputStream();
sOut.write(tbsReq.getEncoded(ASN1Encoding.DER));
sOut.close();
}
catch (Exception e)
{
throw new OCSPException("exception processing TBSRequest: " + e, e);
}
DERBitString bitSig = new DERBitString(contentSigner.getSignature());
AlgorithmIdentifier sigAlgId = contentSigner.getAlgorithmIdentifier();
if (chain != null && chain.length > 0)
{
ASN1EncodableVector v = new ASN1EncodableVector();
for (int i = 0; i != chain.length; i++)
{
v.add(chain[i].toASN1Structure());
}
signature = new Signature(sigAlgId, bitSig, new DERSequence(v));
}
else
{
signature = new Signature(sigAlgId, bitSig);
}
}
return new OCSPReq(new OCSPRequest(tbsReq, signature));
}
/**
* Generate an unsigned request
*
* @return the OCSPReq
* @throws org.bouncycastle.ocsp.OCSPException
*/
public OCSPReq build()
throws OCSPException
{
return generateRequest(null, null);
}
public OCSPReq build(
ContentSigner signer,
X509CertificateHolder[] chain)
throws OCSPException, IllegalArgumentException
{
if (signer == null)
{
throw new IllegalArgumentException("no signer specified");
}
return generateRequest(signer, chain);
}
}

@ -0,0 +1,141 @@
package org.bouncycastle.cert.ocsp;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.asn1.ASN1Exception;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.ocsp.OCSPResponse;
import org.bouncycastle.asn1.ocsp.ResponseBytes;
import org.bouncycastle.cert.CertIOException;
public class OCSPResp
{
public static final int SUCCESSFUL = 0; // Response has valid confirmations
public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request
public static final int INTERNAL_ERROR = 2; // Internal error in issuer
public static final int TRY_LATER = 3; // Try again later
// (4) is not used
public static final int SIG_REQUIRED = 5; // Must sign the request
public static final int UNAUTHORIZED = 6; // Request unauthorized
private OCSPResponse resp;
public OCSPResp(
OCSPResponse resp)
{
this.resp = resp;
}
public OCSPResp(
byte[] resp)
throws IOException
{
this(new ByteArrayInputStream(resp));
}
public OCSPResp(
InputStream resp)
throws IOException
{
this(new ASN1InputStream(resp));
}
private OCSPResp(
ASN1InputStream aIn)
throws IOException
{
try
{
this.resp = OCSPResponse.getInstance(aIn.readObject());
}
catch (IllegalArgumentException e)
{
throw new CertIOException("malformed response: " + e.getMessage(), e);
}
catch (ClassCastException e)
{
throw new CertIOException("malformed response: " + e.getMessage(), e);
}
catch (ASN1Exception e)
{
throw new CertIOException("malformed response: " + e.getMessage(), e);
}
if (resp == null)
{
throw new CertIOException("malformed response: no response data found");
}
}
public int getStatus()
{
return this.resp.getResponseStatus().getValue().intValue();
}
public Object getResponseObject()
throws OCSPException
{
ResponseBytes rb = this.resp.getResponseBytes();
if (rb == null)
{
return null;
}
if (rb.getResponseType().equals(OCSPObjectIdentifiers.id_pkix_ocsp_basic))
{
try
{
ASN1Primitive obj = ASN1Primitive.fromByteArray(rb.getResponse().getOctets());
return new BasicOCSPResp(BasicOCSPResponse.getInstance(obj));
}
catch (Exception e)
{
throw new OCSPException("problem decoding object: " + e, e);
}
}
return rb.getResponse();
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] getEncoded()
throws IOException
{
return resp.getEncoded();
}
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof OCSPResp))
{
return false;
}
OCSPResp r = (OCSPResp)o;
return resp.equals(r.resp);
}
public int hashCode()
{
return resp.hashCode();
}
public OCSPResponse toASN1Structure()
{
return resp;
}
}

@ -0,0 +1,59 @@
package org.bouncycastle.cert.ocsp;
import java.io.IOException;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.ocsp.OCSPResponse;
import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
import org.bouncycastle.asn1.ocsp.ResponseBytes;
/**
* base generator for an OCSP response - at the moment this only supports the
* generation of responses containing BasicOCSP responses.
*/
public class OCSPRespBuilder
{
public static final int SUCCESSFUL = 0; // Response has valid confirmations
public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request
public static final int INTERNAL_ERROR = 2; // Internal error in issuer
public static final int TRY_LATER = 3; // Try again later
// (4) is not used
public static final int SIG_REQUIRED = 5; // Must sign the request
public static final int UNAUTHORIZED = 6; // Request unauthorized
public OCSPResp build(
int status,
Object response)
throws OCSPException
{
if (response == null)
{
return new OCSPResp(new OCSPResponse(new OCSPResponseStatus(status), null));
}
if (response instanceof BasicOCSPResp)
{
BasicOCSPResp r = (BasicOCSPResp)response;
ASN1OctetString octs;
try
{
octs = new DEROctetString(r.getEncoded());
}
catch (IOException e)
{
throw new OCSPException("can't encode object.", e);
}
ResponseBytes rb = new ResponseBytes(
OCSPObjectIdentifiers.id_pkix_ocsp_basic, octs);
return new OCSPResp(new OCSPResponse(
new OCSPResponseStatus(status), rb));
}
throw new OCSPException("unknown response object");
}
}

@ -0,0 +1,64 @@
package org.bouncycastle.cert.ocsp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.cert.X509CertificateHolder;
class OCSPUtils
{
static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0];
static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
static Date extractDate(ASN1GeneralizedTime time)
{
try
{
return time.getDate();
}
catch (Exception e)
{
throw new IllegalStateException("exception processing GeneralizedTime: " + e.getMessage());
}
}
static Set getCriticalExtensionOIDs(Extensions extensions)
{
if (extensions == null)
{
return EMPTY_SET;
}
return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs())));
}
static Set getNonCriticalExtensionOIDs(Extensions extensions)
{
if (extensions == null)
{
return EMPTY_SET;
}
// TODO: should probably produce a set that imposes correct ordering
return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
}
static List getExtensionOIDs(Extensions extensions)
{
if (extensions == null)
{
return EMPTY_LIST;
}
return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs()));
}
}

@ -0,0 +1,25 @@
package org.bouncycastle.cert.ocsp;
import org.bouncycastle.asn1.ocsp.Request;
import org.bouncycastle.asn1.x509.Extensions;
public class Req
{
private Request req;
public Req(
Request req)
{
this.req = req;
}
public CertificateID getCertID()
{
return new CertificateID(req.getReqCert());
}
public Extensions getSingleRequestExtensions()
{
return req.getSingleRequestExtensions();
}
}

@ -0,0 +1,52 @@
package org.bouncycastle.cert.ocsp;
import java.util.Date;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ocsp.ResponseData;
import org.bouncycastle.asn1.ocsp.SingleResponse;
import org.bouncycastle.asn1.x509.Extensions;
public class RespData
{
private ResponseData data;
public RespData(
ResponseData data)
{
this.data = data;
}
public int getVersion()
{
return data.getVersion().getValue().intValue() + 1;
}
public RespID getResponderId()
{
return new RespID(data.getResponderID());
}
public Date getProducedAt()
{
return OCSPUtils.extractDate(data.getProducedAt());
}
public SingleResp[] getResponses()
{
ASN1Sequence s = data.getResponses();
SingleResp[] rs = new SingleResp[s.size()];
for (int i = 0; i != rs.length; i++)
{
rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i)));
}
return rs;
}
public Extensions getResponseExtensions()
{
return data.getResponseExtensions();
}
}

@ -0,0 +1,89 @@
package org.bouncycastle.cert.ocsp;
import java.io.OutputStream;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.ResponderID;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.operator.DigestCalculator;
/**
* Carrier for a ResponderID.
*/
public class RespID
{
public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
ResponderID id;
public RespID(
ResponderID id)
{
this.id = id;
}
public RespID(
X500Name name)
{
this.id = new ResponderID(name);
}
/**
* Calculate a RespID based on the public key of the responder.
*
* @param subjectPublicKeyInfo the info structure for the responder public key.
* @param digCalc a SHA-1 digest calculator.
* @throws OCSPException on exception creating ID.
*/
public RespID(
SubjectPublicKeyInfo subjectPublicKeyInfo,
DigestCalculator digCalc)
throws OCSPException
{
try
{
if (!digCalc.getAlgorithmIdentifier().equals(HASH_SHA1))
{
throw new IllegalArgumentException("only SHA-1 can be used with RespID");
}
OutputStream digOut = digCalc.getOutputStream();
digOut.write(subjectPublicKeyInfo.getPublicKeyData().getBytes());
digOut.close();
this.id = new ResponderID(new DEROctetString(digCalc.getDigest()));
}
catch (Exception e)
{
throw new OCSPException("problem creating ID: " + e, e);
}
}
public ResponderID toASN1Primitive()
{
return id;
}
public boolean equals(
Object o)
{
if (!(o instanceof RespID))
{
return false;
}
RespID obj = (RespID)o;
return id.equals(obj.id);
}
public int hashCode()
{
return id.hashCode();
}
}

@ -0,0 +1,55 @@
package org.bouncycastle.cert.ocsp;
import java.util.Date;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ocsp.RevokedInfo;
import org.bouncycastle.asn1.x509.CRLReason;
/**
* wrapper for the RevokedInfo object
*/
public class RevokedStatus
implements CertificateStatus
{
RevokedInfo info;
public RevokedStatus(
RevokedInfo info)
{
this.info = info;
}
public RevokedStatus(
Date revocationDate,
int reason)
{
this.info = new RevokedInfo(new ASN1GeneralizedTime(revocationDate), CRLReason.lookup(reason));
}
public Date getRevocationTime()
{
return OCSPUtils.extractDate(info.getRevocationTime());
}
public boolean hasRevocationReason()
{
return (info.getRevocationReason() != null);
}
/**
* return the revocation reason. Note: this field is optional, test for it
* with hasRevocationReason() first.
* @return the revocation reason value.
* @exception IllegalStateException if a reason is asked for and none is avaliable
*/
public int getRevocationReason()
{
if (info.getRevocationReason() == null)
{
throw new IllegalStateException("attempt to get a reason where none is available");
}
return info.getRevocationReason().getValue().intValue();
}
}

@ -0,0 +1,102 @@
package org.bouncycastle.cert.ocsp;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ocsp.CertStatus;
import org.bouncycastle.asn1.ocsp.RevokedInfo;
import org.bouncycastle.asn1.ocsp.SingleResponse;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
public class SingleResp
{
private SingleResponse resp;
private Extensions extensions;
public SingleResp(
SingleResponse resp)
{
this.resp = resp;
this.extensions = resp.getSingleExtensions();
}
public CertificateID getCertID()
{
return new CertificateID(resp.getCertID());
}
/**
* Return the status object for the response - null indicates good.
*
* @return the status object for the response, null if it is good.
*/
public CertificateStatus getCertStatus()
{
CertStatus s = resp.getCertStatus();
if (s.getTagNo() == 0)
{
return null; // good
}
else if (s.getTagNo() == 1)
{
return new RevokedStatus(RevokedInfo.getInstance(s.getStatus()));
}
return new UnknownStatus();
}
public Date getThisUpdate()
{
return OCSPUtils.extractDate(resp.getThisUpdate());
}
/**
* return the NextUpdate value - note: this is an optional field so may
* be returned as null.
*
* @return nextUpdate, or null if not present.
*/
public Date getNextUpdate()
{
if (resp.getNextUpdate() == null)
{
return null;
}
return OCSPUtils.extractDate(resp.getNextUpdate());
}
public boolean hasExtensions()
{
return extensions != null;
}
public Extension getExtension(ASN1ObjectIdentifier oid)
{
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
public List getExtensionOIDs()
{
return OCSPUtils.getExtensionOIDs(extensions);
}
public Set getCriticalExtensionOIDs()
{
return OCSPUtils.getCriticalExtensionOIDs(extensions);
}
public Set getNonCriticalExtensionOIDs()
{
return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
}
}

@ -0,0 +1,12 @@
package org.bouncycastle.cert.ocsp;
/**
* wrapper for the UnknownInfo object
*/
public class UnknownStatus
implements CertificateStatus
{
public UnknownStatus()
{
}
}

@ -0,0 +1,18 @@
package org.bouncycastle.cert.ocsp.jcajce;
import java.security.PublicKey;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.operator.DigestCalculator;
public class JcaBasicOCSPRespBuilder
extends BasicOCSPRespBuilder
{
public JcaBasicOCSPRespBuilder(PublicKey key, DigestCalculator digCalc)
throws OCSPException
{
super(SubjectPublicKeyInfo.getInstance(key.getEncoded()), digCalc);
}
}

@ -0,0 +1,20 @@
package org.bouncycastle.cert.ocsp.jcajce;
import java.math.BigInteger;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.operator.DigestCalculator;
public class JcaCertificateID
extends CertificateID
{
public JcaCertificateID(DigestCalculator digestCalculator, X509Certificate issuerCert, BigInteger number)
throws OCSPException, CertificateEncodingException
{
super(digestCalculator, new JcaX509CertificateHolder(issuerCert), number);
}
}

@ -0,0 +1,26 @@
package org.bouncycastle.cert.ocsp.jcajce;
import java.security.PublicKey;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.RespID;
import org.bouncycastle.operator.DigestCalculator;
public class JcaRespID
extends RespID
{
public JcaRespID(X500Principal name)
{
super(X500Name.getInstance(name.getEncoded()));
}
public JcaRespID(PublicKey pubKey, DigestCalculator digCalc)
throws OCSPException
{
super(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()), digCalc);
}
}

@ -0,0 +1,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<body bgcolor="#ffffff">
JCA extensions to the OCSP online certificate status package.
</body>
</html>

@ -4,14 +4,16 @@ import java.io.IOException;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.util.Pack;
class MSOutlookKeyIdCalculator
{
// This is less than ideal, but it seems to be the best way of supporting this without exposing SHA-1
// as the class is only used to workout the MSOutlook Key ID, you can think of the fact it's SHA-1 as
// a coincidence...
static byte[] calculateKeyId(SubjectPublicKeyInfo info)
{
Digest dig = new SHA1Digest(); // TODO: include definition of SHA-1 here
SHA1Digest dig = new SHA1Digest();
byte[] hash = new byte[dig.getDigestSize()];
byte[] spkiEnc = new byte[0];
try
@ -30,4 +32,391 @@ class MSOutlookKeyIdCalculator
return hash;
}
private static abstract class GeneralDigest
{
private static final int BYTE_LENGTH = 64;
private byte[] xBuf;
private int xBufOff;
private long byteCount;
/**
* Standard constructor
*/
protected GeneralDigest()
{
xBuf = new byte[4];
xBufOff = 0;
}
/**
* Copy constructor. We are using copy constructors in place
* of the Object.clone() interface as this interface is not
* supported by J2ME.
*/
protected GeneralDigest(GeneralDigest t)
{
xBuf = new byte[t.xBuf.length];
copyIn(t);
}
protected void copyIn(GeneralDigest t)
{
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
xBufOff = t.xBufOff;
byteCount = t.byteCount;
}
public void update(
byte in)
{
xBuf[xBufOff++] = in;
if (xBufOff == xBuf.length)
{
processWord(xBuf, 0);
xBufOff = 0;
}
byteCount++;
}
public void update(
byte[] in,
int inOff,
int len)
{
//
// fill the current word
//
while ((xBufOff != 0) && (len > 0))
{
update(in[inOff]);
inOff++;
len--;
}
//
// process whole words.
//
while (len > xBuf.length)
{
processWord(in, inOff);
inOff += xBuf.length;
len -= xBuf.length;
byteCount += xBuf.length;
}
//
// load in the remainder.
//
while (len > 0)
{
update(in[inOff]);
inOff++;
len--;
}
}
public void finish()
{
long bitLength = (byteCount << 3);
//
// add the pad bytes.
//
update((byte)128);
while (xBufOff != 0)
{
update((byte)0);
}
processLength(bitLength);
processBlock();
}
public void reset()
{
byteCount = 0;
xBufOff = 0;
for (int i = 0; i < xBuf.length; i++)
{
xBuf[i] = 0;
}
}
protected abstract void processWord(byte[] in, int inOff);
protected abstract void processLength(long bitLength);
protected abstract void processBlock();
}
private static class SHA1Digest
extends GeneralDigest
{
private static final int DIGEST_LENGTH = 20;
private int H1, H2, H3, H4, H5;
private int[] X = new int[80];
private int xOff;
/**
* Standard constructor
*/
public SHA1Digest()
{
reset();
}
public String getAlgorithmName()
{
return "SHA-1";
}
public int getDigestSize()
{
return DIGEST_LENGTH;
}
protected void processWord(
byte[] in,
int inOff)
{
// Note: Inlined for performance
// X[xOff] = Pack.bigEndianToInt(in, inOff);
int n = in[ inOff] << 24;
n |= (in[++inOff] & 0xff) << 16;
n |= (in[++inOff] & 0xff) << 8;
n |= (in[++inOff] & 0xff);
X[xOff] = n;
if (++xOff == 16)
{
processBlock();
}
}
protected void processLength(
long bitLength)
{
if (xOff > 14)
{
processBlock();
}
X[14] = (int)(bitLength >>> 32);
X[15] = (int)(bitLength & 0xffffffff);
}
public int doFinal(
byte[] out,
int outOff)
{
finish();
Pack.intToBigEndian(H1, out, outOff);
Pack.intToBigEndian(H2, out, outOff + 4);
Pack.intToBigEndian(H3, out, outOff + 8);
Pack.intToBigEndian(H4, out, outOff + 12);
Pack.intToBigEndian(H5, out, outOff + 16);
reset();
return DIGEST_LENGTH;
}
/**
* reset the chaining variables
*/
public void reset()
{
super.reset();
H1 = 0x67452301;
H2 = 0xefcdab89;
H3 = 0x98badcfe;
H4 = 0x10325476;
H5 = 0xc3d2e1f0;
xOff = 0;
for (int i = 0; i != X.length; i++)
{
X[i] = 0;
}
}
//
// Additive constants
//
private static final int Y1 = 0x5a827999;
private static final int Y2 = 0x6ed9eba1;
private static final int Y3 = 0x8f1bbcdc;
private static final int Y4 = 0xca62c1d6;
private int f(
int u,
int v,
int w)
{
return ((u & v) | ((~u) & w));
}
private int h(
int u,
int v,
int w)
{
return (u ^ v ^ w);
}
private int g(
int u,
int v,
int w)
{
return ((u & v) | (u & w) | (v & w));
}
protected void processBlock()
{
//
// expand 16 word block into 80 word block.
//
for (int i = 16; i < 80; i++)
{
int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16];
X[i] = t << 1 | t >>> 31;
}
//
// set up working variables.
//
int A = H1;
int B = H2;
int C = H3;
int D = H4;
int E = H5;
//
// round 1
//
int idx = 0;
for (int j = 0; j < 4; j++)
{
// E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1
// B = rotateLeft(B, 30)
E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1;
B = B << 30 | B >>> 2;
D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1;
A = A << 30 | A >>> 2;
C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1;
E = E << 30 | E >>> 2;
B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1;
D = D << 30 | D >>> 2;
A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1;
C = C << 30 | C >>> 2;
}
//
// round 2
//
for (int j = 0; j < 4; j++)
{
// E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2
// B = rotateLeft(B, 30)
E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2;
B = B << 30 | B >>> 2;
D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2;
A = A << 30 | A >>> 2;
C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2;
E = E << 30 | E >>> 2;
B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2;
D = D << 30 | D >>> 2;
A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2;
C = C << 30 | C >>> 2;
}
//
// round 3
//
for (int j = 0; j < 4; j++)
{
// E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3
// B = rotateLeft(B, 30)
E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3;
B = B << 30 | B >>> 2;
D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3;
A = A << 30 | A >>> 2;
C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3;
E = E << 30 | E >>> 2;
B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3;
D = D << 30 | D >>> 2;
A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3;
C = C << 30 | C >>> 2;
}
//
// round 4
//
for (int j = 0; j <= 3; j++)
{
// E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4
// B = rotateLeft(B, 30)
E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4;
B = B << 30 | B >>> 2;
D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4;
A = A << 30 | A >>> 2;
C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4;
E = E << 30 | E >>> 2;
B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4;
D = D << 30 | D >>> 2;
A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4;
C = C << 30 | C >>> 2;
}
H1 += A;
H2 += B;
H3 += C;
H4 += D;
H5 += E;
//
// reset start of the buffer.
//
xOff = 0;
for (int i = 0; i < 16; i++)
{
X[i] = 0;
}
}
}
}

@ -1,18 +1,20 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.cms.AttributeTable;
import java.util.Map;
import org.bouncycastle.asn1.cms.AttributeTable;
/**
* Note: The SIGNATURE parameter is only available when generating unsigned attributes.
*/
public interface CMSAttributeTableGenerator
{
static final String CONTENT_TYPE = "contentType";
static final String DIGEST = "digest";
static final String SIGNATURE = "encryptedDigest";
static final String DIGEST_ALGORITHM_IDENTIFIER = "digestAlgID";
String CONTENT_TYPE = "contentType";
String DIGEST = "digest";
String SIGNATURE = "encryptedDigest";
String DIGEST_ALGORITHM_IDENTIFIER = "digestAlgID";
String MAC_ALGORITHM_IDENTIFIER = "macAlgID";
String SIGNATURE_ALGORITHM_IDENTIFIER = "signatureAlgID";
AttributeTable getAttributes(Map parameters)
throws CMSAttributeTableGenerationException;

@ -5,10 +5,15 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@ -20,9 +25,9 @@ import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
import org.bouncycastle.util.Encodable;
import org.bouncycastle.util.Store;
/**
@ -54,6 +59,7 @@ import org.bouncycastle.util.Store;
* </pre>
*/
public class CMSSignedData
implements Encodable
{
private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
@ -182,11 +188,18 @@ public class CMSSignedData
// this can happen if the signed message is sent simply to send a
// certificate chain.
//
if (signedData.getEncapContentInfo().getContent() != null)
ASN1Encodable content = signedData.getEncapContentInfo().getContent();
if (content != null)
{
this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(),
((ASN1OctetString)(signedData.getEncapContentInfo()
.getContent())).getOctets());
if (content instanceof ASN1OctetString)
{
this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(),
((ASN1OctetString)content).getOctets());
}
else
{
this.signedContent = new PKCS7ProcessableObject(signedData.getEncapContentInfo().getContentType(), content);
}
}
else
{
@ -229,7 +242,6 @@ public class CMSSignedData
{
ASN1Set s = signedData.getSignerInfos();
List signerInfos = new ArrayList();
SignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder();
for (int i = 0; i != s.size(); i++)
{
@ -255,6 +267,26 @@ public class CMSSignedData
return signerInfoStore;
}
/**
* Return if this is object represents a detached signature.
*
* @return true if this message represents a detached signature, false otherwise.
*/
public boolean isDetachedSignature()
{
return signedData.getEncapContentInfo().getContent() == null && signedData.getSignerInfos().size() > 0;
}
/**
* Return if this is object represents a certificate management message.
*
* @return true if the message has no signers or content, false otherwise.
*/
public boolean isCertificateManagementMessage()
{
return signedData.getEncapContentInfo().getContent() == null && signedData.getSignerInfos().size() == 0;
}
/**
* Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
*
@ -300,6 +332,23 @@ public class CMSSignedData
// }
// END android-removed
/**
* Return the digest algorithm identifiers for the SignedData object
*
* @return the set of digest algorithm identifiers
*/
public Set<AlgorithmIdentifier> getDigestAlgorithmIDs()
{
Set<AlgorithmIdentifier> digests = new HashSet<AlgorithmIdentifier>(signedData.getDigestAlgorithms().size());
for (Enumeration en = signedData.getDigestAlgorithms().getObjects(); en.hasMoreElements();)
{
digests.add(AlgorithmIdentifier.getInstance(en.nextElement()));
}
return Collections.unmodifiableSet(digests);
}
/**
* Return the a string representation of the OID associated with the
* encapsulated content info structure carried in the signed data.
@ -347,7 +396,7 @@ public class CMSSignedData
// {
// return verifySignatures(verifierProvider, false);
// }
//
//
// /**
// * Verify all the SignerInformation objects and optionally their associated counter signatures attached
// * to this CMS SignedData object.
@ -361,30 +410,27 @@ public class CMSSignedData
// throws CMSException
// {
// Collection signers = this.getSignerInfos().getSigners();
//
//
// for (Iterator it = signers.iterator(); it.hasNext();)
// {
// SignerInformation signer = (SignerInformation)it.next();
//
//
// try
// {
// SignerInformationVerifier verifier = verifierProvider.get(signer.getSID());
//
//
// if (!signer.verify(verifier))
// {
// return false;
// }
//
//
// if (!ignoreCounterSignatures)
// {
// Collection counterSigners = signer.getCounterSignatures().getSigners();
//
//
// for (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
// {
// SignerInformation counterSigner = (SignerInformation)cIt.next();
// SignerInformationVerifier counterVerifier = verifierProvider.get(signer.getSID());
//
// if (!counterSigner.verify(counterVerifier))
// if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
// {
// return false;
// }
@ -396,7 +442,29 @@ public class CMSSignedData
// throw new CMSException("failure in verifier provider: " + e.getMessage(), e);
// }
// }
//
//
// return true;
// }
//
// private boolean verifyCounterSignature(SignerInformation counterSigner, SignerInformationVerifierProvider verifierProvider)
// throws OperatorCreationException, CMSException
// {
// SignerInformationVerifier counterVerifier = verifierProvider.get(counterSigner.getSID());
//
// if (!counterSigner.verify(counterVerifier))
// {
// return false;
// }
//
// Collection counterSigners = counterSigner.getCounterSignatures().getSigners();
// for (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
// {
// if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
// {
// return false;
// }
// }
//
// return true;
// }
// END android-removed
@ -475,7 +543,7 @@ public class CMSSignedData
* @param signedData the signed data object to be used as a base.
* @param certificates the new certificates to be used.
* @param attrCerts the new attribute certificates to be used.
* @param crls the new CRLs to be used.
* @param revocations the new CRLs to be used - a collection of X509CRLHolder objects, OtherRevocationInfoFormat, or both.
* @return a new signed data object.
* @exception CMSException if there is an error processing the CertStore
*/
@ -483,7 +551,7 @@ public class CMSSignedData
CMSSignedData signedData,
Store certificates,
Store attrCerts,
Store crls)
Store revocations)
throws CMSException
{
//
@ -492,7 +560,7 @@ public class CMSSignedData
CMSSignedData cms = new CMSSignedData(signedData);
//
// replace the certs and crls in the SignedData object
// replace the certs and revocations in the SignedData object
//
ASN1Set certSet = null;
ASN1Set crlSet = null;
@ -518,9 +586,9 @@ public class CMSSignedData
}
}
if (crls != null)
if (revocations != null)
{
ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(crls));
ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(revocations));
if (set.size() != 0)
{

@ -140,7 +140,7 @@ public class CMSSignedDataGenerator
ASN1OctetString octs = null;
if (content != null)
if (content.getContent() != null)
{
ByteArrayOutputStream bOut = null;

@ -5,16 +5,20 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.BEROctetStringGenerator;
import org.bouncycastle.asn1.BERSet;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
@ -23,18 +27,65 @@ import org.bouncycastle.asn1.cms.ContentInfo;
// import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
// import org.bouncycastle.asn1.ocsp.OCSPResponse;
// import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
// import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
// import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.io.Streams;
import org.bouncycastle.util.io.TeeInputStream;
import org.bouncycastle.util.io.TeeOutputStream;
class CMSUtils
{
private static final Set<String> des = new HashSet<String>();
static
{
des.add("DES");
des.add("DESEDE");
// BEGIN android-removed
// des.add(OIWObjectIdentifiers.desCBC.getId());
// des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
// des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
// des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId());
// END android-removed
}
static boolean isDES(String algorithmID)
{
String name = Strings.toUpperCase(algorithmID);
return des.contains(name);
}
static boolean isEquivalent(AlgorithmIdentifier algId1, AlgorithmIdentifier algId2)
{
if (algId1 == null || algId2 == null)
{
return false;
}
if (!algId1.getAlgorithm().equals(algId2.getAlgorithm()))
{
return false;
}
ASN1Encodable params1 = algId1.getParameters();
ASN1Encodable params2 = algId2.getParameters();
if (params1 != null)
{
return params1.equals(params2) || (params1.equals(DERNull.INSTANCE) && params2 == null);
}
return params2 == null || params2.equals(DERNull.INSTANCE);
}
static ContentInfo readContentInfo(
byte[] input)
throws CMSException
@ -99,18 +150,37 @@ class CMSUtils
static List getCRLsFromStore(Store crlStore)
throws CMSException
{
List certs = new ArrayList();
List crls = new ArrayList();
try
{
for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext();)
{
X509CRLHolder c = (X509CRLHolder)it.next();
certs.add(c.toASN1Structure());
Object rev = it.next();
if (rev instanceof X509CRLHolder)
{
X509CRLHolder c = (X509CRLHolder)rev;
crls.add(c.toASN1Structure());
}
// BEGIN android-removed
// else if (rev instanceof OtherRevocationInfoFormat)
// {
// OtherRevocationInfoFormat infoFormat = OtherRevocationInfoFormat.getInstance(rev);
//
// validateInfoFormat(infoFormat);
//
// crls.add(new DERTaggedObject(false, 1, infoFormat));
// }
// END android-removed
else if (rev instanceof ASN1TaggedObject)
{
crls.add(rev);
}
}
return certs;
return crls;
}
catch (ClassCastException e)
{
@ -119,6 +189,19 @@ class CMSUtils
}
// BEGIN android-removed
// private static void validateInfoFormat(OtherRevocationInfoFormat infoFormat)
// {
// if (CMSObjectIdentifiers.id_ri_ocsp_response.equals(infoFormat.getInfoFormat()))
// {
// OCSPResponse resp = OCSPResponse.getInstance(infoFormat.getInfo());
//
// if (resp.getResponseStatus().getValue().intValue() != OCSPResponseStatus.SUCCESSFUL)
// {
// throw new IllegalArgumentException("cannot add unsuccessful OCSP response to CMS SignedData");
// }
// }
// }
//
// static Collection getOthersFromStore(ASN1ObjectIdentifier otherRevocationInfoFormat, Store otherRevocationInfos)
// {
// List others = new ArrayList();
@ -126,18 +209,10 @@ class CMSUtils
// for (Iterator it = otherRevocationInfos.getMatches(null).iterator(); it.hasNext();)
// {
// ASN1Encodable info = (ASN1Encodable)it.next();
// OtherRevocationInfoFormat infoFormat = new OtherRevocationInfoFormat(otherRevocationInfoFormat, info);
// validateInfoFormat(infoFormat);
//
// if (CMSObjectIdentifiers.id_ri_ocsp_response.equals(otherRevocationInfoFormat))
// {
// OCSPResponse resp = OCSPResponse.getInstance(info);
//
// if (resp.getResponseStatus().getValue().intValue() != OCSPResponseStatus.SUCCESSFUL)
// {
// throw new IllegalArgumentException("cannot add unsuccessful OCSP response to CMS SignedData");
// }
// }
//
// others.add(new DERTaggedObject(false, 1, new OtherRevocationInfoFormat(otherRevocationInfoFormat, info)));
// others.add(new DERTaggedObject(false, 1, infoFormat));
// }
//
// return others;
@ -202,7 +277,7 @@ class CMSUtils
throw new CMSException("Malformed content.", e);
}
}
public static byte[] streamToByteArray(
InputStream in)
throws IOException

@ -5,6 +5,7 @@ import java.util.Map;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
// BEGIN android-removed
// import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
@ -51,6 +52,11 @@ public class DefaultCMSSignatureAlgorithmNameGenerator
addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, "RIPEMD128", "RSA");
addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, "RIPEMD160", "RSA");
addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, "RIPEMD256", "RSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
@ -66,6 +72,14 @@ public class DefaultCMSSignatureAlgorithmNameGenerator
addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
// BEGIN android-removed
// addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1", "PLAIN-ECDSA");
// addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224", "PLAIN-ECDSA");
// addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256", "PLAIN-ECDSA");
// addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384", "PLAIN-ECDSA");
// addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512", "PLAIN-ECDSA");
// addEntries(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160", "PLAIN-ECDSA");
// END android-removed
encryptionAlgs.put(X9ObjectIdentifiers.id_dsa, "DSA");
encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
@ -159,6 +173,13 @@ public class DefaultCMSSignatureAlgorithmNameGenerator
public String getSignatureName(AlgorithmIdentifier digestAlg, AlgorithmIdentifier encryptionAlg)
{
String digestName = getDigestAlgName(encryptionAlg.getAlgorithm());
if (!digestName.equals(encryptionAlg.getAlgorithm().getId()))
{
return digestName + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
}
return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
}
}

@ -10,8 +10,10 @@ import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAlgorithmProtection;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
/**
* Default signed attributes generator.
@ -93,6 +95,14 @@ public class DefaultSignedAttributeTableGenerator
std.put(attr.getAttrType(), attr);
}
if (!std.contains(CMSAttributes.cmsAlgorithmProtect))
{
Attribute attr = new Attribute(CMSAttributes.cmsAlgorithmProtect, new DERSet(new CMSAlgorithmProtection(
(AlgorithmIdentifier)parameters.get(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER),
CMSAlgorithmProtection.SIGNATURE, (AlgorithmIdentifier)parameters.get(CMSAttributeTableGenerator.SIGNATURE_ALGORITHM_IDENTIFIER))));
std.put(attr.getAttrType(), attr);
}
return std;
}

@ -0,0 +1,65 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
public class PKCS7ProcessableObject
implements CMSTypedData
{
private final ASN1ObjectIdentifier type;
private final ASN1Encodable structure;
public PKCS7ProcessableObject(
ASN1ObjectIdentifier type,
ASN1Encodable structure)
{
this.type = type;
this.structure = structure;
}
public ASN1ObjectIdentifier getContentType()
{
return type;
}
public void write(OutputStream cOut)
throws IOException, CMSException
{
if (structure instanceof ASN1Sequence)
{
ASN1Sequence s = ASN1Sequence.getInstance(structure);
for (Iterator it = s.iterator(); it.hasNext();)
{
ASN1Encodable enc = (ASN1Encodable)it.next();
cOut.write(enc.toASN1Primitive().getEncoded(ASN1Encoding.DER));
}
}
else
{
byte[] encoded = structure.toASN1Primitive().getEncoded(ASN1Encoding.DER);
int index = 1;
while ((encoded[index] & 0xff) > 127)
{
index++;
}
index++;
cOut.write(encoded, index, encoded.length - index);
}
}
public Object getContent()
{
return structure;
}
}

@ -0,0 +1,20 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface PasswordRecipient
extends Recipient
{
public static final int PKCS5_SCHEME2 = 0;
public static final int PKCS5_SCHEME2_UTF8 = 1;
byte[] calculateDerivedKey(int schemeID, AlgorithmIdentifier derivationAlgorithm, int keySize)
throws CMSException;
RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier contentEncryptionAlgorithm, byte[] derivedKey, byte[] encryptedEncryptedContentKey)
throws CMSException;
int getPasswordConversionScheme();
char[] getPassword();
}

@ -0,0 +1,5 @@
package org.bouncycastle.cms;
public interface Recipient
{
}

@ -0,0 +1,48 @@
package org.bouncycastle.cms;
import java.io.InputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.InputDecryptor;
import org.bouncycastle.operator.MacCalculator;
import org.bouncycastle.util.io.TeeInputStream;
public class RecipientOperator
{
private final AlgorithmIdentifier algorithmIdentifier;
private final Object operator;
public RecipientOperator(InputDecryptor decryptor)
{
this.algorithmIdentifier = decryptor.getAlgorithmIdentifier();
this.operator = decryptor;
}
public RecipientOperator(MacCalculator macCalculator)
{
this.algorithmIdentifier = macCalculator.getAlgorithmIdentifier();
this.operator = macCalculator;
}
public InputStream getInputStream(InputStream dataIn)
{
if (operator instanceof InputDecryptor)
{
return ((InputDecryptor)operator).getInputStream(dataIn);
}
else
{
return new TeeInputStream(dataIn, ((MacCalculator)operator).getOutputStream());
}
}
public boolean isMacBased()
{
return operator instanceof MacCalculator;
}
public byte[] getMac()
{
return ((MacCalculator)operator).getMac();
}
}

@ -183,13 +183,15 @@ public class SignerInfoGenerator
*/
ASN1Set signedAttr = null;
AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier());
AlgorithmIdentifier digestAlg = null;
if (sAttrGen != null)
{
digestAlg = digester.getAlgorithmIdentifier();
calculatedDigest = digester.getDigest();
Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), calculatedDigest);
Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), digestEncryptionAlgorithm, calculatedDigest);
AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
signedAttr = getAttributeSet(signed);
@ -220,7 +222,7 @@ public class SignerInfoGenerator
ASN1Set unsignedAttr = null;
if (unsAttrGen != null)
{
Map parameters = getBaseParameters(contentType, digestAlg, calculatedDigest);
Map parameters = getBaseParameters(contentType, digestAlg, digestEncryptionAlgorithm, calculatedDigest);
parameters.put(CMSAttributeTableGenerator.SIGNATURE, Arrays.clone(sigBytes));
AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
@ -228,8 +230,6 @@ public class SignerInfoGenerator
unsignedAttr = getAttributeSet(unsigned);
}
AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier());
return new SignerInfo(signerIdentifier, digestAlg,
signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr);
}
@ -255,7 +255,7 @@ public class SignerInfoGenerator
return null;
}
private Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
private Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, AlgorithmIdentifier sigAlgId, byte[] hash)
{
Map param = new HashMap();
@ -265,7 +265,9 @@ public class SignerInfoGenerator
}
param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
param.put(CMSAttributeTableGenerator.SIGNATURE_ALGORITHM_IDENTIFIER, sigAlgId);
param.put(CMSAttributeTableGenerator.DIGEST, Arrays.clone(hash));
return param;
}

@ -18,6 +18,7 @@ import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAlgorithmProtection;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignerIdentifier;
@ -38,21 +39,22 @@ import org.bouncycastle.util.io.TeeOutputStream;
*/
public class SignerInformation
{
private SignerId sid;
private SignerInfo info;
private AlgorithmIdentifier digestAlgorithm;
private AlgorithmIdentifier encryptionAlgorithm;
private final ASN1Set signedAttributeSet;
private final ASN1Set unsignedAttributeSet;
private CMSProcessable content;
private byte[] signature;
private ASN1ObjectIdentifier contentType;
private byte[] resultDigest;
private final SignerId sid;
private final CMSProcessable content;
private final byte[] signature;
private final ASN1ObjectIdentifier contentType;
private final boolean isCounterSignature;
// Derived
private AttributeTable signedAttributeValues;
private AttributeTable unsignedAttributeValues;
private boolean isCounterSignature;
private AttributeTable signedAttributeValues;
private AttributeTable unsignedAttributeValues;
private byte[] resultDigest;
protected final SignerInfo info;
protected final AlgorithmIdentifier digestAlgorithm;
protected final AlgorithmIdentifier encryptionAlgorithm;
protected final ASN1Set signedAttributeSet;
protected final ASN1Set unsignedAttributeSet;
SignerInformation(
SignerInfo info,
@ -89,6 +91,28 @@ public class SignerInformation
this.resultDigest = resultDigest;
}
/**
* Protected constructor. In some cases clients have their own idea about how to encode
* the signed attributes and calculate the signature. This constructor is to allow developers
* to deal with that by extending off the class and overridng methods like getSignedAttributes().
*
* @param baseInfo the SignerInformation to base this one on.
*/
protected SignerInformation(SignerInformation baseInfo)
{
this.info = baseInfo.info;
this.contentType = baseInfo.contentType;
this.isCounterSignature = baseInfo.isCounterSignature();
this.sid = baseInfo.getSID();
this.digestAlgorithm = info.getDigestAlgorithm();
this.signedAttributeSet = info.getAuthenticatedAttributes();
this.unsignedAttributeSet = info.getUnauthenticatedAttributes();
this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm();
this.signature = info.getEncryptedDigest().getOctets();
this.content = baseInfo.content;
this.resultDigest = baseInfo.resultDigest;
}
public boolean isCounterSignature()
{
return isCounterSignature;
@ -302,7 +326,7 @@ public class SignerInformation
{
if (signedAttributeSet != null)
{
return signedAttributeSet.getEncoded();
return signedAttributeSet.getEncoded(ASN1Encoding.DER);
}
return null;
@ -428,6 +452,46 @@ public class SignerInformation
}
}
AttributeTable signedAttrTable = this.getSignedAttributes();
// RFC 6211 Validate Algorithm Identifier protection attribute if present
{
AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
if (unsignedAttrTable != null && unsignedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect).size() > 0)
{
throw new CMSException("A cmsAlgorithmProtect attribute MUST be a signed attribute");
}
if (signedAttrTable != null)
{
ASN1EncodableVector protectionAttributes = signedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect);
if (protectionAttributes.size() > 1)
{
throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present");
}
if (protectionAttributes.size() > 0)
{
Attribute attr = Attribute.getInstance(protectionAttributes.get(0));
if (attr.getAttrValues().size() != 1)
{
throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value");
}
CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]);
if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), info.getDigestAlgorithm()))
{
throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm");
}
if (!CMSUtils.isEquivalent(algorithmProtection.getSignatureAlgorithm(), info.getDigestEncryptionAlgorithm()))
{
throw new CMSException("CMS Algorithm Identifier Protection check failed for signatureAlgorithm");
}
}
}
}
// RFC 3852 11.2 Check the message-digest attribute is correct
{
ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
@ -457,7 +521,6 @@ public class SignerInformation
// RFC 3852 11.4 Validate countersignature attribute(s)
{
AttributeTable signedAttrTable = this.getSignedAttributes();
if (signedAttrTable != null
&& signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
{
@ -470,7 +533,7 @@ public class SignerInformation
ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
for (int i = 0; i < csAttrs.size(); ++i)
{
Attribute csAttr = (Attribute)csAttrs.get(i);
Attribute csAttr = Attribute.getInstance(csAttrs.get(i));
if (csAttr.getAttrValues().size() < 1)
{
throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");

@ -7,13 +7,37 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bouncycastle.util.Iterable;
public class SignerInformationStore
implements Iterable<SignerInformation>
{
private List all = new ArrayList();
private Map table = new HashMap();
/**
* Create a store containing a single SignerInformation object.
*
* @param signerInfo the signer information to contain.
*/
public SignerInformationStore(
SignerInformation signerInfo)
{
this.all = new ArrayList(1);
this.all.add(signerInfo);
SignerId sid = signerInfo.getSID();
table.put(sid, all);
}
/**
* Create a store containing a collection of SignerInformation objects.
*
* @param signerInfos a collection signer information objects to contain.
*/
public SignerInformationStore(
Collection signerInfos)
Collection<SignerInformation> signerInfos)
{
Iterator it = signerInfos.iterator();
@ -65,7 +89,7 @@ public class SignerInformationStore
*
* @return a collection of signers.
*/
public Collection getSigners()
public Collection<SignerInformation> getSigners()
{
return new ArrayList(all);
}
@ -76,7 +100,7 @@ public class SignerInformationStore
* @param selector a signer id to select against.
* @return a collection of SignerInformation objects.
*/
public Collection getSigners(
public Collection<SignerInformation> getSigners(
SignerId selector)
{
if (selector.getIssuer() != null && selector.getSubjectKeyIdentifier() != null)
@ -106,4 +130,12 @@ public class SignerInformationStore
return list == null ? new ArrayList() : new ArrayList(list);
}
}
/**
* Support method for Iterable where available.
*/
public Iterator<SignerInformation> iterator()
{
return getSigners().iterator();
}
}

@ -0,0 +1,132 @@
package org.bouncycastle.openssl;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
public class CertificateTrustBlock
{
private ASN1Sequence uses;
private ASN1Sequence prohibitions;
private String alias;
public CertificateTrustBlock(Set<ASN1ObjectIdentifier> uses)
{
this(null, uses, null);
}
public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses)
{
this(alias, uses, null);
}
public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses, Set<ASN1ObjectIdentifier> prohibitions)
{
this.alias = alias;
this.uses = toSequence(uses);
this.prohibitions = toSequence(prohibitions);
}
CertificateTrustBlock(byte[] encoded)
{
ASN1Sequence seq = ASN1Sequence.getInstance(encoded);
for (Enumeration en = seq.getObjects(); en.hasMoreElements();)
{
ASN1Encodable obj = (ASN1Encodable)en.nextElement();
if (obj instanceof ASN1Sequence)
{
this.uses = ASN1Sequence.getInstance(obj);
}
else if (obj instanceof ASN1TaggedObject)
{
this.prohibitions = ASN1Sequence.getInstance((ASN1TaggedObject)obj, false);
}
else if (obj instanceof DERUTF8String)
{
this.alias = DERUTF8String.getInstance(obj).getString();
}
}
}
public String getAlias()
{
return alias;
}
public Set<ASN1ObjectIdentifier> getUses()
{
return toSet(uses);
}
public Set<ASN1ObjectIdentifier> getProhibitions()
{
return toSet(prohibitions);
}
private Set<ASN1ObjectIdentifier> toSet(ASN1Sequence seq)
{
if (seq != null)
{
Set<ASN1ObjectIdentifier> oids = new HashSet<ASN1ObjectIdentifier>(seq.size());
for (Enumeration en = seq.getObjects(); en.hasMoreElements(); )
{
oids.add(ASN1ObjectIdentifier.getInstance(en.nextElement()));
}
return oids;
}
return Collections.EMPTY_SET;
}
private ASN1Sequence toSequence(Set<ASN1ObjectIdentifier> oids)
{
if (oids == null || oids.isEmpty())
{
return null;
}
ASN1EncodableVector v = new ASN1EncodableVector();
for (Iterator it = oids.iterator(); it.hasNext();)
{
v.add((ASN1Encodable)it.next());
}
return new DERSequence(v);
}
ASN1Sequence toASN1Sequence()
{
ASN1EncodableVector v = new ASN1EncodableVector();
if (uses != null)
{
v.add(uses);
}
if (prohibitions != null)
{
v.add(new DERTaggedObject(false, 0, prohibitions));
}
if (alias != null)
{
v.add(new DERUTF8String(alias));
}
return new DERSequence(v);
}
}

@ -72,6 +72,20 @@ public class DefaultDigestAlgorithmIdentifierFinder
digestNameToOids.put("SHA-512", NISTObjectIdentifiers.id_sha512);
// BEGIN android-removed
// digestNameToOids.put("SHA1", OIWObjectIdentifiers.idSHA1);
// digestNameToOids.put("SHA224", NISTObjectIdentifiers.id_sha224);
// digestNameToOids.put("SHA256", NISTObjectIdentifiers.id_sha256);
// digestNameToOids.put("SHA384", NISTObjectIdentifiers.id_sha384);
// digestNameToOids.put("SHA512", NISTObjectIdentifiers.id_sha512);
// digestNameToOids.put("SHA3-224", NISTObjectIdentifiers.id_sha3_224);
// digestNameToOids.put("SHA3-256", NISTObjectIdentifiers.id_sha3_256);
// digestNameToOids.put("SHA3-384", NISTObjectIdentifiers.id_sha3_384);
// digestNameToOids.put("SHA3-512", NISTObjectIdentifiers.id_sha3_512);
//
// digestNameToOids.put("SHAKE-128", NISTObjectIdentifiers.id_shake128);
// digestNameToOids.put("SHAKE-256", NISTObjectIdentifiers.id_shake256);
//
// digestNameToOids.put("GOST3411", CryptoProObjectIdentifiers.gostR3411);
//
// digestNameToOids.put("MD2", PKCSObjectIdentifiers.md2);
@ -106,4 +120,4 @@ public class DefaultDigestAlgorithmIdentifierFinder
{
return new AlgorithmIdentifier((ASN1ObjectIdentifier)digestNameToOids.get(digAlgName), DERNull.INSTANCE);
}
}
}

@ -10,7 +10,9 @@ import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
// BEGIN android-removed
// import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@ -88,8 +90,18 @@ public class DefaultSignatureAlgorithmIdentifierFinder
// algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
// algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
// algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
// algorithms.put("SHA384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA384);
// algorithms.put("SHA512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA512);
// algorithms.put("RIPEMD160WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160);
// algorithms.put("SHA1WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
// algorithms.put("SHA224WITHPCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
// algorithms.put("SHA256WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_256);
// algorithms.put("SHA384WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_384);
// algorithms.put("SHA512WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_512);
// END android-removed
//
// According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
// The parameters field SHALL be NULL for RSA based signature algorithms.
@ -227,4 +239,4 @@ public class DefaultSignatureAlgorithmIdentifierFinder
{
return generate(sigAlgName);
}
}
}

@ -0,0 +1,41 @@
package org.bouncycastle.operator;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public class GenericKey
{
private AlgorithmIdentifier algorithmIdentifier;
private Object representation;
/**
* @deprecated provide an AlgorithmIdentifier.
* @param representation key data
*/
public GenericKey(Object representation)
{
this.algorithmIdentifier = null;
this.representation = representation;
}
public GenericKey(AlgorithmIdentifier algorithmIdentifier, byte[] representation)
{
this.algorithmIdentifier = algorithmIdentifier;
this.representation = representation;
}
protected GenericKey(AlgorithmIdentifier algorithmIdentifier, Object representation)
{
this.algorithmIdentifier = algorithmIdentifier;
this.representation = representation;
}
public AlgorithmIdentifier getAlgorithmIdentifier()
{
return algorithmIdentifier;
}
public Object getRepresentation()
{
return representation;
}
}

@ -0,0 +1,29 @@
package org.bouncycastle.operator;
import java.io.InputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
/**
* General interface for an operator that is able to produce
* an InputStream that will decrypt a stream of encrypted data.
*/
public interface InputDecryptor
{
/**
* Return the algorithm identifier describing the encryption
* algorithm and parameters this decryptor can process.
*
* @return algorithm oid and parameters.
*/
AlgorithmIdentifier getAlgorithmIdentifier();
/**
* Wrap the passed in input stream encIn, returning an input stream
* that decrypts what it reads from encIn before returning it.
*
* @param encIn InputStream containing encrypted input.
* @return an decrypting InputStream
*/
InputStream getInputStream(InputStream encIn);
}

@ -0,0 +1,34 @@
package org.bouncycastle.operator;
import java.io.OutputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface MacCalculator
{
AlgorithmIdentifier getAlgorithmIdentifier();
/**
* Returns a stream that will accept data for the purpose of calculating
* the MAC for later verification. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
* the data on the fly as well.
*
* @return an OutputStream
*/
OutputStream getOutputStream();
/**
* Return the calculated MAC based on what has been written to the stream.
*
* @return calculated MAC.
*/
byte[] getMac();
/**
* Return the key used for calculating the MAC.
*
* @return the MAC key.
*/
GenericKey getKey();
}

@ -10,9 +10,9 @@ import java.security.Signature;
import java.security.SignatureException;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;

@ -15,9 +15,9 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.OperatorCreationException;
@ -190,9 +190,10 @@ public class JcaContentVerifierProviderBuilder
private class SigVerifier
implements ContentVerifier
{
private SignatureOutputStream stream;
private AlgorithmIdentifier algorithm;
protected SignatureOutputStream stream;
SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream)
{
this.algorithm = algorithm;
@ -239,6 +240,27 @@ public class JcaContentVerifierProviderBuilder
this.rawSignature = rawSignature;
}
public boolean verify(byte[] expected)
{
try
{
return super.verify(expected);
}
finally
{
// we need to do this as in some PKCS11 implementations the session associated with the init of the
// raw signature will not be freed if verify is not called on it.
try
{
rawSignature.verify(expected);
}
catch (Exception e)
{
// ignore
}
}
}
public boolean verify(byte[] digest, byte[] expected)
{
try
@ -251,6 +273,19 @@ public class JcaContentVerifierProviderBuilder
{
throw new RuntimeOperatorException("exception obtaining raw signature: " + e.getMessage(), e);
}
finally
{
// we need to do this as in some PKCS11 implementations the session associated with the init of the
// standard signature will not be freed if verify is not called on it.
try
{
stream.verify(expected);
}
catch (Exception e)
{
// ignore
}
}
}
}

@ -7,9 +7,9 @@ import java.security.MessageDigest;
import java.security.Provider;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;

@ -25,7 +25,9 @@ import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
// BEGIN android-removed
// import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@ -38,9 +40,11 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jcajce.JcaJceHelper;
import org.bouncycastle.jcajce.JcaJceUtils;
import org.bouncycastle.jcajce.util.AlgorithmParametersUtils;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.jcajce.util.MessageDigestUtils;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.util.Integers;
class OperatorHelper
{
@ -48,6 +52,7 @@ class OperatorHelper
private static final Map asymmetricWrapperAlgNames = new HashMap();
private static final Map symmetricWrapperAlgNames = new HashMap();
private static final Map symmetricKeyAlgNames = new HashMap();
private static final Map symmetricWrapperKeySizes = new HashMap();
static
{
@ -62,6 +67,17 @@ class OperatorHelper
// BEGIN android-removed
// oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
// oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
// oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
// oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
// oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
// oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384WITHPLAIN-ECDSA");
// oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512WITHPLAIN-ECDSA");
// oids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160WITHPLAIN-ECDSA");
// oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
// oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
// oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
// oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
// oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
// END android-removed
oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
@ -84,9 +100,9 @@ class OperatorHelper
oids.put(NISTObjectIdentifiers.id_sha256, "SHA-256");
oids.put(NISTObjectIdentifiers.id_sha384, "SHA-384");
oids.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
oids.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128");
oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160");
oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-256");
oids.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
@ -101,6 +117,16 @@ class OperatorHelper
symmetricWrapperAlgNames.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, "SEEDWrap");
symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
symmetricWrapperKeySizes.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, Integers.valueOf(192));
symmetricWrapperKeySizes.put(NISTObjectIdentifiers.id_aes128_wrap, Integers.valueOf(128));
symmetricWrapperKeySizes.put(NISTObjectIdentifiers.id_aes192_wrap, Integers.valueOf(192));
symmetricWrapperKeySizes.put(NISTObjectIdentifiers.id_aes256_wrap, Integers.valueOf(256));
symmetricWrapperKeySizes.put(NTTObjectIdentifiers.id_camellia128_wrap, Integers.valueOf(128));
symmetricWrapperKeySizes.put(NTTObjectIdentifiers.id_camellia192_wrap, Integers.valueOf(192));
symmetricWrapperKeySizes.put(NTTObjectIdentifiers.id_camellia256_wrap, Integers.valueOf(256));
symmetricWrapperKeySizes.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, Integers.valueOf(128));
symmetricWrapperKeySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC, Integers.valueOf(192));
symmetricKeyAlgNames.put(NISTObjectIdentifiers.aes, "AES");
symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes128_CBC, "AES");
symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes192_CBC, "AES");
@ -116,6 +142,16 @@ class OperatorHelper
this.helper = helper;
}
String getWrappingAlgorithmName(ASN1ObjectIdentifier algOid)
{
return (String)symmetricWrapperAlgNames.get(algOid);
}
int getKeySizeInBits(ASN1ObjectIdentifier algOid)
{
return ((Integer)symmetricWrapperKeySizes.get(algOid)).intValue();
}
Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames)
throws OperatorCreationException
{
@ -235,7 +271,7 @@ class OperatorHelper
try
{
dig = helper.createDigest(getDigestAlgName(digAlgId.getAlgorithm()));
dig = helper.createDigest(MessageDigestUtils.getDigestName(digAlgId.getAlgorithm()));
}
catch (NoSuchAlgorithmException e)
{
@ -305,7 +341,7 @@ class OperatorHelper
{
AlgorithmParameters params = helper.createAlgorithmParameters(algName);
JcaJceUtils.loadParameters(params, algorithm.getParameters());
AlgorithmParametersUtils.loadParameters(params, algorithm.getParameters());
PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class);
sig.setParameter(spec);
@ -329,7 +365,7 @@ class OperatorHelper
if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
{
RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
return getDigestName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
}
}
@ -341,55 +377,18 @@ class OperatorHelper
return sigAlgId.getAlgorithm().getId();
}
private static String getDigestAlgName(
ASN1ObjectIdentifier digestAlgOID)
// we need to remove the - to create a correct signature name
private static String getDigestName(ASN1ObjectIdentifier oid)
{
if (PKCSObjectIdentifiers.md5.equals(digestAlgOID))
{
return "MD5";
}
else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID))
{
return "SHA1";
}
else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
{
return "SHA224";
}
else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
{
return "SHA256";
}
else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID))
{
return "SHA384";
}
else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID))
{
return "SHA512";
}
// BEGIN android-removed
// else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
// {
// return "RIPEMD128";
// }
// else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
// {
// return "RIPEMD160";
// }
// else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
// {
// return "RIPEMD256";
// }
// else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
// {
// return "GOST3411";
// }
// END android-removed
else
String name = MessageDigestUtils.getDigestName(oid);
int dIndex = name.indexOf('-');
if (dIndex > 0)
{
return digestAlgOID.getId();
return name.substring(0, dIndex) + name.substring(dIndex + 1);
}
return MessageDigestUtils.getDigestName(oid);
}
public X509Certificate convertCertificate(X509CertificateHolder certHolder)
@ -406,10 +405,6 @@ class OperatorHelper
{
throw new OpCertificateException("cannot get encoded form of certificate: " + e.getMessage(), e);
}
catch (NoSuchAlgorithmException e)
{
throw new OpCertificateException("cannot create certificate factory: " + e.getMessage(), e);
}
catch (NoSuchProviderException e)
{
throw new OpCertificateException("cannot find factory provider: " + e.getMessage(), e);

@ -0,0 +1,226 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import org.bouncycastle.util.Arrays;
/**
* Base class for an application specific object
*/
public abstract class ASN1ApplicationSpecific
extends ASN1Primitive
{
protected final boolean isConstructed;
protected final int tag;
protected final byte[] octets;
ASN1ApplicationSpecific(
boolean isConstructed,
int tag,
byte[] octets)
{
this.isConstructed = isConstructed;
this.tag = tag;
this.octets = octets;
}
/**
* Return an ASN1ApplicationSpecific from the passed in object, which may be a byte array, or null.
*
* @param obj the object to be converted.
* @return obj's representation as an ASN1ApplicationSpecific object.
*/
public static ASN1ApplicationSpecific getInstance(Object obj)
{
if (obj == null || obj instanceof ASN1ApplicationSpecific)
{
return (ASN1ApplicationSpecific)obj;
}
else if (obj instanceof byte[])
{
try
{
return ASN1ApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
}
catch (IOException e)
{
throw new IllegalArgumentException("Failed to construct object from byte[]: " + e.getMessage());
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
protected static int getLengthOfHeader(byte[] data)
{
int length = data[1] & 0xff; // TODO: assumes 1 byte tag
if (length == 0x80)
{
return 2; // indefinite-length encoding
}
if (length > 127)
{
int size = length & 0x7f;
// Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
if (size > 4)
{
throw new IllegalStateException("DER length more than 4 bytes: " + size);
}
return size + 2;
}
return 2;
}
/**
* Return true if the object is marked as constructed, false otherwise.
*
* @return true if constructed, otherwise false.
*/
public boolean isConstructed()
{
return isConstructed;
}
/**
* Return the contents of this object as a byte[]
*
* @return the encoded contents of the object.
*/
public byte[] getContents()
{
return octets;
}
/**
* Return the tag number associated with this object,
*
* @return the application tag number.
*/
public int getApplicationTag()
{
return tag;
}
/**
* Return the enclosed object assuming explicit tagging.
*
* @return the resulting object
* @throws IOException if reconstruction fails.
*/
public ASN1Primitive getObject()
throws IOException
{
return new ASN1InputStream(getContents()).readObject();
}
/**
* Return the enclosed object assuming implicit tagging.
*
* @param derTagNo the type tag that should be applied to the object's contents.
* @return the resulting object
* @throws IOException if reconstruction fails.
*/
public ASN1Primitive getObject(int derTagNo)
throws IOException
{
if (derTagNo >= 0x1f)
{
throw new IOException("unsupported tag number");
}
byte[] orig = this.getEncoded();
byte[] tmp = replaceTagNumber(derTagNo, orig);
if ((orig[0] & BERTags.CONSTRUCTED) != 0)
{
tmp[0] |= BERTags.CONSTRUCTED;
}
return new ASN1InputStream(tmp).readObject();
}
int encodedLength()
throws IOException
{
return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
}
/* (non-Javadoc)
* @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
*/
void encode(ASN1OutputStream out) throws IOException
{
int classBits = BERTags.APPLICATION;
if (isConstructed)
{
classBits |= BERTags.CONSTRUCTED;
}
out.writeEncoded(classBits, tag, octets);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1ApplicationSpecific))
{
return false;
}
ASN1ApplicationSpecific other = (ASN1ApplicationSpecific)o;
return isConstructed == other.isConstructed
&& tag == other.tag
&& Arrays.areEqual(octets, other.octets);
}
public int hashCode()
{
return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
}
private byte[] replaceTagNumber(int newTag, byte[] input)
throws IOException
{
int tagNo = input[0] & 0x1f;
int index = 1;
//
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
//
if (tagNo == 0x1f)
{
tagNo = 0;
int b = input[index++] & 0xff;
// X.690-0207 8.1.2.4.2
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
if ((b & 0x7f) == 0) // Note: -1 will pass
{
throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
}
while ((b >= 0) && ((b & 0x80) != 0))
{
tagNo |= (b & 0x7f);
tagNo <<= 7;
b = input[index++] & 0xff;
}
// tagNo |= (b & 0x7f);
}
byte[] tmp = new byte[input.length - index + 1];
System.arraycopy(input, index, tmp, 1, tmp.length - 1);
tmp[0] = (byte)newTag;
return tmp;
}
}

@ -2,9 +2,18 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Interface to parse ASN.1 application specific objects.
*/
public interface ASN1ApplicationSpecificParser
extends ASN1Encodable, InMemoryRepresentable
{
/**
* Read the next object in the parser.
*
* @return an ASN1Encodable
* @throws IOException on a parsing or decoding error.
*/
ASN1Encodable readObject()
throws IOException;
}

@ -0,0 +1,291 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.Streams;
/**
* Base class for BIT STRING objects
*/
public abstract class ASN1BitString
extends ASN1Primitive
implements ASN1String
{
private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
protected final byte[] data;
protected final int padBits;
/**
* @param bitString an int containing the BIT STRING
* @return the correct number of pad bits for a bit string defined in
* a 32 bit constant
*/
static protected int getPadBits(
int bitString)
{
int val = 0;
for (int i = 3; i >= 0; i--)
{
//
// this may look a little odd, but if it isn't done like this pre jdk1.2
// JVM's break!
//
if (i != 0)
{
if ((bitString >> (i * 8)) != 0)
{
val = (bitString >> (i * 8)) & 0xFF;
break;
}
}
else
{
if (bitString != 0)
{
val = bitString & 0xFF;
break;
}
}
}
if (val == 0)
{
return 0;
}
int bits = 1;
while (((val <<= 1) & 0xFF) != 0)
{
bits++;
}
return 8 - bits;
}
/**
* @param bitString an int containing the BIT STRING
* @return the correct number of bytes for a bit string defined in
* a 32 bit constant
*/
static protected byte[] getBytes(int bitString)
{
if (bitString == 0)
{
return new byte[0];
}
int bytes = 4;
for (int i = 3; i >= 1; i--)
{
if ((bitString & (0xFF << (i * 8))) != 0)
{
break;
}
bytes--;
}
byte[] result = new byte[bytes];
for (int i = 0; i < bytes; i++)
{
result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
}
return result;
}
/**
* Base constructor.
*
* @param data the octets making up the bit string.
* @param padBits the number of extra bits at the end of the string.
*/
public ASN1BitString(
byte[] data,
int padBits)
{
if (data == null)
{
throw new NullPointerException("data cannot be null");
}
if (data.length == 0 && padBits != 0)
{
throw new IllegalArgumentException("zero length data with non-zero pad bits");
}
if (padBits > 7 || padBits < 0)
{
throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
}
this.data = Arrays.clone(data);
this.padBits = padBits;
}
/**
* Return a String representation of this BIT STRING
*
* @return a String representation.
*/
public String getString()
{
StringBuffer buf = new StringBuffer("#");
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
try
{
aOut.writeObject(this);
}
catch (IOException e)
{
throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e);
}
byte[] string = bOut.toByteArray();
for (int i = 0; i != string.length; i++)
{
buf.append(table[(string[i] >>> 4) & 0xf]);
buf.append(table[string[i] & 0xf]);
}
return buf.toString();
}
/**
* @return the value of the bit string as an int (truncating if necessary)
*/
public int intValue()
{
int value = 0;
byte[] string = data;
if (padBits > 0 && data.length <= 4)
{
string = derForm(data, padBits);
}
for (int i = 0; i != string.length && i != 4; i++)
{
value |= (string[i] & 0xff) << (8 * i);
}
return value;
}
/**
* Return the octets contained in this BIT STRING, checking that this BIT STRING really
* does represent an octet aligned string. Only use this method when the standard you are
* following dictates that the BIT STRING will be octet aligned.
*
* @return a copy of the octet aligned data.
*/
public byte[] getOctets()
{
if (padBits != 0)
{
throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
}
return Arrays.clone(data);
}
public byte[] getBytes()
{
return derForm(data, padBits);
}
public int getPadBits()
{
return padBits;
}
public String toString()
{
return getString();
}
public int hashCode()
{
return padBits ^ Arrays.hashCode(this.getBytes());
}
protected boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1BitString))
{
return false;
}
ASN1BitString other = (ASN1BitString)o;
return this.padBits == other.padBits
&& Arrays.areEqual(this.getBytes(), other.getBytes());
}
protected static byte[] derForm(byte[] data, int padBits)
{
byte[] rv = Arrays.clone(data);
// DER requires pad bits be zero
if (padBits > 0)
{
rv[data.length - 1] &= 0xff << padBits;
}
return rv;
}
static ASN1BitString fromInputStream(int length, InputStream stream)
throws IOException
{
if (length < 1)
{
throw new IllegalArgumentException("truncated BIT STRING detected");
}
int padBits = stream.read();
byte[] data = new byte[length - 1];
if (data.length != 0)
{
if (Streams.readFully(stream, data) != data.length)
{
throw new EOFException("EOF encountered in middle of BIT STRING");
}
if (padBits > 0 && padBits < 8)
{
if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xff << padBits)))
{
return new DLBitString(data, padBits);
}
}
}
return new DERBitString(data, padBits);
}
public ASN1Primitive getLoadedObject()
{
return this.toASN1Primitive();
}
ASN1Primitive toDERObject()
{
return new DERBitString(data, padBits);
}
ASN1Primitive toDLObject()
{
return new DLBitString(data, padBits);
}
abstract void encode(ASN1OutputStream out)
throws IOException;
}

@ -1,15 +1,220 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import org.bouncycastle.util.Arrays;
/**
* Public facade of ASN.1 Boolean data.
* <p>
* Use following to place a new instance of ASN.1 Boolean in your dataset:
* <ul>
* <li> ASN1Boolean.TRUE literal</li>
* <li> ASN1Boolean.FALSE literal</li>
* <li> {@link ASN1Boolean#getInstance(boolean) ASN1Boolean.getInstance(boolean)}</li>
* <li> {@link ASN1Boolean#getInstance(int) ASN1Boolean.getInstance(int)}</li>
* </ul>
* </p>
*/
public class ASN1Boolean
extends DERBoolean
extends ASN1Primitive
{
public ASN1Boolean(boolean value)
private static final byte[] TRUE_VALUE = new byte[] { (byte)0xff };
private static final byte[] FALSE_VALUE = new byte[] { 0 };
private final byte[] value;
public static final ASN1Boolean FALSE = new ASN1Boolean(false);
public static final ASN1Boolean TRUE = new ASN1Boolean(true);
/**
* return a boolean from the passed in object.
*
* @param obj an ASN1Boolean or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return an ASN1Boolean instance.
*/
public static ASN1Boolean getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Boolean)
{
return (ASN1Boolean)obj;
}
if (obj instanceof byte[])
{
byte[] enc = (byte[])obj;
try
{
return (ASN1Boolean)fromByteArray(enc);
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct boolean from byte[]: " + e.getMessage());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an ASN1Boolean from the passed in boolean.
* @param value true or false depending on the ASN1Boolean wanted.
* @return an ASN1Boolean instance.
*/
public static ASN1Boolean getInstance(
boolean value)
{
return (value ? TRUE : FALSE);
}
/**
* return an ASN1Boolean from the passed in value.
* @param value non-zero (true) or zero (false) depending on the ASN1Boolean wanted.
* @return an ASN1Boolean instance.
*/
public static ASN1Boolean getInstance(
int value)
{
super(value);
return (value != 0 ? TRUE : FALSE);
}
ASN1Boolean(byte[] value)
// BEGIN android-added
/**
* return a ASN1Boolean from the passed in array.
*/
public static ASN1Boolean getInstance(
byte[] octets)
{
super(value);
return (octets[0] != 0) ? TRUE : FALSE;
}
// END android-added
/**
* return a Boolean from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1Boolean instance.
*/
public static ASN1Boolean getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof ASN1Boolean)
{
return getInstance(o);
}
else
{
return ASN1Boolean.fromOctetString(((ASN1OctetString)o).getOctets());
}
}
// BEGIN android-changed
protected ASN1Boolean(
// END android-changed
byte[] value)
{
if (value.length != 1)
{
throw new IllegalArgumentException("byte value should have 1 byte in it");
}
if (value[0] == 0)
{
this.value = FALSE_VALUE;
}
else if ((value[0] & 0xff) == 0xff)
{
this.value = TRUE_VALUE;
}
else
{
this.value = Arrays.clone(value);
}
}
/**
* @deprecated use getInstance(boolean) method.
* @param value true or false.
*/
// BEGIN android-changed
protected ASN1Boolean(
boolean value)
// END android-changed
{
this.value = (value) ? TRUE_VALUE : FALSE_VALUE;
}
public boolean isTrue()
{
return (value[0] != 0);
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
return 3;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.BOOLEAN, value);
}
protected boolean asn1Equals(
ASN1Primitive o)
{
if (o instanceof ASN1Boolean)
{
return (value[0] == ((ASN1Boolean)o).value[0]);
}
return false;
}
public int hashCode()
{
return value[0];
}
public String toString()
{
return (value[0] != 0) ? "TRUE" : "FALSE";
}
static ASN1Boolean fromOctetString(byte[] value)
{
if (value.length != 1)
{
throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
}
if (value[0] == 0)
{
return FALSE;
}
else if ((value[0] & 0xff) == 0xff)
{
return TRUE;
}
else
{
return new ASN1Boolean(value);
}
}
}

@ -5,8 +5,22 @@ package org.bouncycastle.asn1;
* own object any attempt to tag the object implicitly will convert the tag to
* an explicit one as the encoding rules require.
* <p>
* If you use this interface your class should also implement the getInstance
* pattern which takes a tag object and the tagging mode used.
* If you use this interface your class should also implement the getInstance()
* pattern which takes a tag object and the tagging mode used.
* </p>
* <hr>
* <p><b>X.690</b></p>
* <p><b>8: Basic encoding rules</b></p>
* <p><b>8.13 Encoding of a choice value </b></p>
* <p>
* The encoding of a choice value shall be the same as the encoding of a value of the chosen type.
* <blockquote>
* NOTE 1 &mdash; The encoding may be primitive or constructed depending on the chosen type.
* <br />
* NOTE 2 &mdash; The tag used in the identifier octets is the tag of the chosen type,
* as specified in the ASN.1 definition of the choice type.
* </blockquote>
* </p>
*/
public interface ASN1Choice
{

@ -1,6 +1,13 @@
package org.bouncycastle.asn1;
/**
* Basic interface to produce serialisers for ASN.1 encodings.
*/
public interface ASN1Encodable
{
/**
* Return an object, possibly constructed, of ASN.1 primitives
* @return an ASN.1 primitive.
*/
ASN1Primitive toASN1Primitive();
}

@ -3,19 +3,35 @@ package org.bouncycastle.asn1;
import java.util.Enumeration;
import java.util.Vector;
/**
* Mutable class for building ASN.1 constructed objects.
*/
public class ASN1EncodableVector
{
Vector v = new Vector();
private final Vector v = new Vector();
/**
* Base constructor.
*/
public ASN1EncodableVector()
{
}
/**
* Add an encodable to the vector.
*
* @param obj the encodable to add.
*/
public void add(ASN1Encodable obj)
{
v.addElement(obj);
}
/**
* Add the contents of another vector.
*
* @param other the vector to add.
*/
public void addAll(ASN1EncodableVector other)
{
for (Enumeration en = other.v.elements(); en.hasMoreElements();)
@ -24,11 +40,22 @@ public class ASN1EncodableVector
}
}
/**
* Return the object at position i in this vector.
*
* @param i the index of the object of interest.
* @return the object at position i.
*/
public ASN1Encodable get(int i)
{
return (ASN1Encodable)v.elementAt(i);
}
/**
* Return the size of the vector.
*
* @return the object count in the vector.
*/
public int size()
{
return v.size();

@ -1,8 +1,22 @@
package org.bouncycastle.asn1;
/**
* Supported encoding formats.
*/
public interface ASN1Encoding
{
/**
* DER - distinguished encoding rules.
*/
static final String DER = "DER";
/**
* DL - definite length encoding.
*/
static final String DL = "DL";
/**
* BER - basic encoding rules.
*/
static final String BER = "BER";
}

@ -1,22 +1,174 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.util.Arrays;
/**
* Class representing the ASN.1 ENUMERATED type.
*/
public class ASN1Enumerated
extends DEREnumerated
extends ASN1Primitive
{
ASN1Enumerated(byte[] bytes)
private final byte[] bytes;
/**
* return an enumerated from the passed in object
*
* @param obj an ASN1Enumerated or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return an ASN1Enumerated instance, or null.
*/
public static ASN1Enumerated getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Enumerated)
{
return (ASN1Enumerated)obj;
}
if (obj instanceof byte[])
{
try
{
return (ASN1Enumerated)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an Enumerated from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1Enumerated instance, or null.
*/
public static ASN1Enumerated getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof ASN1Enumerated)
{
return getInstance(o);
}
else
{
return fromOctetString(((ASN1OctetString)o).getOctets());
}
}
/**
* Constructor from int.
*
* @param value the value of this enumerated.
*/
public ASN1Enumerated(
int value)
{
bytes = BigInteger.valueOf(value).toByteArray();
}
/**
* Constructor from BigInteger
*
* @param value the value of this enumerated.
*/
public ASN1Enumerated(
BigInteger value)
{
bytes = value.toByteArray();
}
/**
* Constructor from encoded BigInteger.
*
* @param bytes the value of this enumerated as an encoded BigInteger (signed).
*/
public ASN1Enumerated(
byte[] bytes)
{
this.bytes = bytes;
}
public BigInteger getValue()
{
super(bytes);
return new BigInteger(bytes);
}
public ASN1Enumerated(BigInteger value)
boolean isConstructed()
{
super(value);
return false;
}
public ASN1Enumerated(int value)
int encodedLength()
{
super(value);
return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.ENUMERATED, bytes);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1Enumerated))
{
return false;
}
ASN1Enumerated other = (ASN1Enumerated)o;
return Arrays.areEqual(this.bytes, other.bytes);
}
public int hashCode()
{
return Arrays.hashCode(bytes);
}
private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
static ASN1Enumerated fromOctetString(byte[] enc)
{
if (enc.length > 1)
{
return new ASN1Enumerated(Arrays.clone(enc));
}
if (enc.length == 0)
{
throw new IllegalArgumentException("ENUMERATED has zero length");
}
int value = enc[0] & 0xff;
if (value >= cache.length)
{
return new ASN1Enumerated(Arrays.clone(enc));
}
ASN1Enumerated possibleMatch = cache[value];
if (possibleMatch == null)
{
possibleMatch = cache[value] = new ASN1Enumerated(Arrays.clone(enc));
}
return possibleMatch;
}
}

@ -1,22 +1,401 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
/**
* Base class representing the ASN.1 GeneralizedTime type.
* <p>
* The main difference between these and UTC time is a 4 digit year.
* </p>
*/
public class ASN1GeneralizedTime
extends DERGeneralizedTime
extends ASN1Primitive
{
ASN1GeneralizedTime(byte[] bytes)
private byte[] time;
/**
* return a generalized time from the passed in object
*
* @param obj an ASN1GeneralizedTime or an object that can be converted into one.
* @return an ASN1GeneralizedTime instance, or null.
* @throws IllegalArgumentException if the object cannot be converted.
*/
public static ASN1GeneralizedTime getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1GeneralizedTime)
{
return (ASN1GeneralizedTime)obj;
}
if (obj instanceof byte[])
{
try
{
return (ASN1GeneralizedTime)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return a Generalized Time object from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @return an ASN1GeneralizedTime instance.
* @throws IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1GeneralizedTime getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof ASN1GeneralizedTime)
{
return getInstance(o);
}
else
{
return new ASN1GeneralizedTime(((ASN1OctetString)o).getOctets());
}
}
/**
* The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
* for local time, or Z+-HHMM on the end, for difference between local
* time and UTC time. The fractional second amount f must consist of at
* least one number with trailing zeroes removed.
*
* @param time the time string.
* @throws IllegalArgumentException if String is an illegal format.
*/
public ASN1GeneralizedTime(
String time)
{
this.time = Strings.toByteArray(time);
try
{
this.getDate();
}
catch (ParseException e)
{
throw new IllegalArgumentException("invalid date string: " + e.getMessage());
}
}
/**
* Base constructor from a java.util.date object
*
* @param time a date object representing the time of interest.
*/
public ASN1GeneralizedTime(
Date time)
{
// BEGIN android-changed
// Was: SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", Locale.US);
// END android-changed
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
this.time = Strings.toByteArray(dateF.format(time));
}
/**
* Base constructor from a java.util.date and Locale - you may need to use this if the default locale
* doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
*
* @param time a date object representing the time of interest.
* @param locale an appropriate Locale for producing an ASN.1 GeneralizedTime value.
*/
public ASN1GeneralizedTime(
Date time,
Locale locale)
{
// BEGIN android-changed
// Was: SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", locale);
SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", Locale.US);
dateF.setCalendar(Calendar.getInstance(Locale.US));
// END android-changed
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
this.time = Strings.toByteArray(dateF.format(time));
}
ASN1GeneralizedTime(
byte[] bytes)
{
this.time = bytes;
}
/**
* Return the time.
*
* @return The time string as it appeared in the encoded object.
*/
public String getTimeString()
{
return Strings.fromByteArray(time);
}
/**
* return the time - always in the form of
* YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
* <p>
* Normally in a certificate we would expect "Z" rather than "GMT",
* however adding the "GMT" means we can just use:
* <pre>
* dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
* </pre>
* To read in the time and get a date which is compatible with our local
* time zone.
* </p>
* @return a String representation of the time.
*/
public String getTime()
{
String stime = Strings.fromByteArray(time);
//
// standardise the format.
//
if (stime.charAt(stime.length() - 1) == 'Z')
{
return stime.substring(0, stime.length() - 1) + "GMT+00:00";
}
else
{
int signPos = stime.length() - 5;
char sign = stime.charAt(signPos);
if (sign == '-' || sign == '+')
{
return stime.substring(0, signPos)
+ "GMT"
+ stime.substring(signPos, signPos + 3)
+ ":"
+ stime.substring(signPos + 3);
}
else
{
signPos = stime.length() - 3;
sign = stime.charAt(signPos);
if (sign == '-' || sign == '+')
{
return stime.substring(0, signPos)
+ "GMT"
+ stime.substring(signPos)
+ ":00";
}
}
}
return stime + calculateGMTOffset();
}
private String calculateGMTOffset()
{
String sign = "+";
TimeZone timeZone = TimeZone.getDefault();
int offset = timeZone.getRawOffset();
if (offset < 0)
{
sign = "-";
offset = -offset;
}
int hours = offset / (60 * 60 * 1000);
int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000);
try
{
if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate()))
{
hours += sign.equals("+") ? 1 : -1;
}
}
catch (ParseException e)
{
// we'll do our best and ignore daylight savings
}
return "GMT" + sign + convert(hours) + ":" + convert(minutes);
}
private String convert(int time)
{
if (time < 10)
{
return "0" + time;
}
return Integer.toString(time);
}
public Date getDate()
throws ParseException
{
SimpleDateFormat dateF;
String stime = Strings.fromByteArray(time);
String d = stime;
if (stime.endsWith("Z"))
{
if (hasFractionalSeconds())
{
// BEGIN android-changed
// Was: dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", Locale.US);
// END android-changed
}
else
{
// BEGIN android-changed
// Was: dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", Locale.US);
// END android-changed
}
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
}
else if (stime.indexOf('-') > 0 || stime.indexOf('+') > 0)
{
d = this.getTime();
if (hasFractionalSeconds())
{
// BEGIN android-changed
// Was: dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz");
dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz", Locale.US);
// END android-changed
}
else
{
// BEGIN android-changed
// Was: dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
dateF = new SimpleDateFormat("yyyyMMddHHmmssz", Locale.US);
// END android-changed
}
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
}
else
{
if (hasFractionalSeconds())
{
// BEGIN android-changed
// dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS", Locale.US);
// END android-changed
}
else
{
// BEGIN android-changed
// Was: dateF = new SimpleDateFormat("yyyyMMddHHmmss");
dateF = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
// END android-changed
}
dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
}
if (hasFractionalSeconds())
{
// java misinterprets extra digits as being milliseconds...
String frac = d.substring(14);
int index;
for (index = 1; index < frac.length(); index++)
{
char ch = frac.charAt(index);
if (!('0' <= ch && ch <= '9'))
{
break;
}
}
if (index - 1 > 3)
{
frac = frac.substring(0, 4) + frac.substring(index);
d = d.substring(0, 14) + frac;
}
else if (index - 1 == 1)
{
frac = frac.substring(0, index) + "00" + frac.substring(index);
d = d.substring(0, 14) + frac;
}
else if (index - 1 == 2)
{
frac = frac.substring(0, index) + "0" + frac.substring(index);
d = d.substring(0, 14) + frac;
}
}
return dateF.parse(d);
}
private boolean hasFractionalSeconds()
{
for (int i = 0; i != time.length; i++)
{
if (time[i] == '.')
{
if (i == 14)
{
return true;
}
}
}
return false;
}
boolean isConstructed()
{
super(bytes);
return false;
}
public ASN1GeneralizedTime(Date time)
int encodedLength()
{
super(time);
int length = time.length;
return 1 + StreamUtil.calculateBodyLength(length) + length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.GENERALIZED_TIME, time);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1GeneralizedTime))
{
return false;
}
return Arrays.areEqual(time, ((ASN1GeneralizedTime)o).time);
}
public ASN1GeneralizedTime(String time)
public int hashCode()
{
super(time);
return Arrays.hashCode(time);
}
}

@ -124,6 +124,12 @@ public class ASN1InputStream
/**
* build an object given its tag and the number of bytes to construct it from.
*
* @param tag the full tag details.
* @param tagNo the tagNo defined.
* @param length the length of the object.
* @return the resulting primitive.
* @throws java.io.IOException on processing exception.
*/
protected ASN1Primitive buildObject(
int tag,
@ -230,11 +236,11 @@ public class ASN1InputStream
//
int length = readLength();
if (length < 0) // indefinite length method
if (length < 0) // indefinite-length method
{
if (!isConstructed)
{
throw new IOException("indefinite length primitive encoding encountered");
throw new IOException("indefinite-length primitive encoding encountered");
}
IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
@ -424,7 +430,7 @@ public class ASN1InputStream
switch (tagNo)
{
case BIT_STRING:
return DERBitString.fromInputStream(defIn.getRemaining(), defIn);
return ASN1BitString.fromInputStream(defIn.getRemaining(), defIn);
case BMP_STRING:
return new DERBMPString(getBMPCharBuffer(defIn));
case BOOLEAN:
@ -438,7 +444,7 @@ public class ASN1InputStream
case IA5_STRING:
return new DERIA5String(defIn.toByteArray());
case INTEGER:
return new ASN1Integer(defIn.toByteArray());
return new ASN1Integer(defIn.toByteArray(), false);
case NULL:
return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?)
case NUMERIC_STRING:
@ -459,6 +465,10 @@ public class ASN1InputStream
return new DERUTF8String(defIn.toByteArray());
case VISIBLE_STRING:
return new DERVisibleString(defIn.toByteArray());
case GRAPHIC_STRING:
return new DERGraphicString(defIn.toByteArray());
case VIDEOTEX_STRING:
return new DERVideotexString(defIn.toByteArray());
default:
throw new IOException("unknown tag " + tagNo + " encountered");
}

@ -1,22 +1,157 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.util.Arrays;
/**
* Class representing the ASN.1 INTEGER type.
*/
public class ASN1Integer
extends DERInteger
extends ASN1Primitive
{
ASN1Integer(byte[] bytes)
private final byte[] bytes;
/**
* return an integer from the passed in object
*
* @param obj an ASN1Integer or an object that can be converted into one.
* @throws IllegalArgumentException if the object cannot be converted.
* @return an ASN1Integer instance.
*/
public static ASN1Integer getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Integer)
{
return (ASN1Integer)obj;
}
if (obj instanceof byte[])
{
try
{
return (ASN1Integer)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an Integer from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @throws IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1Integer instance.
*/
public static ASN1Integer getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof ASN1Integer)
{
return getInstance(o);
}
else
{
return new ASN1Integer(ASN1OctetString.getInstance(obj.getObject()).getOctets());
}
}
public ASN1Integer(
long value)
{
super(bytes);
bytes = BigInteger.valueOf(value).toByteArray();
}
public ASN1Integer(BigInteger value)
public ASN1Integer(
BigInteger value)
{
super(value);
bytes = value.toByteArray();
}
public ASN1Integer(long value)
public ASN1Integer(
byte[] bytes)
{
super(value);
this(bytes, true);
}
ASN1Integer(byte[] bytes, boolean clone)
{
this.bytes = (clone) ? Arrays.clone(bytes) : bytes;
}
public BigInteger getValue()
{
return new BigInteger(bytes);
}
/**
* in some cases positive values get crammed into a space,
* that's not quite big enough...
* @return the BigInteger that results from treating this ASN.1 INTEGER as unsigned.
*/
public BigInteger getPositiveValue()
{
return new BigInteger(1, bytes);
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.INTEGER, bytes);
}
public int hashCode()
{
int value = 0;
for (int i = 0; i != bytes.length; i++)
{
value ^= (bytes[i] & 0xff) << (i % 4);
}
return value;
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1Integer))
{
return false;
}
ASN1Integer other = (ASN1Integer)o;
return Arrays.areEqual(bytes, other.bytes);
}
public String toString()
{
return getValue().toString();
}
}

@ -3,20 +3,32 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* A NULL object.
* A NULL object - use DERNull.INSTANCE for populating structures.
*/
public abstract class ASN1Null
extends ASN1Primitive
{
/**
* @deprecated use DERNull.INSTANCE
*/
// BEGIN android-changed
// BEGIN android-added
/*package*/ ASN1Null()
{
}
// END android-changed
// END android-added
/**
* Return an instance of ASN.1 NULL from the passed in object.
* <p>
* Accepted inputs:
* <ul>
* <li> null &rarr; null
* <li> {@link ASN1Null} object
* <li> a byte[] containing ASN.1 NULL object
* </ul>
* </p>
*
* @param o object to be converted.
* @return an instance of ASN1Null, or null.
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1Null getInstance(Object o)
{
if (o instanceof ASN1Null)

@ -3,8 +3,13 @@ package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.bouncycastle.util.Encodable;
/**
* Base class for defining an ASN.1 object.
*/
public abstract class ASN1Object
implements ASN1Encodable
implements ASN1Encodable, Encodable
{
/**
* Return the default BER or DER encoding for this object.
@ -88,10 +93,21 @@ public abstract class ASN1Object
return this.toASN1Primitive();
}
/**
* Return true if obj is a byte array and represents an object with the given tag value.
*
* @param obj object of interest.
* @param tagValue tag value to check for.
* @return true if obj is a byte encoding starting with the given tag value, false otherwise.
*/
protected static boolean hasEncodedTagValue(Object obj, int tagValue)
{
return (obj instanceof byte[]) && ((byte[])obj)[0] == tagValue;
}
/**
* Method providing a primitive representation of this object suitable for encoding.
* @return a primitive representation of this object.
*/
public abstract ASN1Primitive toASN1Primitive();
}

@ -1,21 +1,219 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.util.Arrays;
/**
* Class representing the ASN.1 OBJECT IDENTIFIER type.
*/
public class ASN1ObjectIdentifier
extends DERObjectIdentifier
extends ASN1Primitive
{
public ASN1ObjectIdentifier(String identifier)
private final String identifier;
private byte[] body;
/**
* return an OID from the passed in object
* @param obj an ASN1ObjectIdentifier or an object that can be converted into one.
* @throws IllegalArgumentException if the object cannot be converted.
* @return an ASN1ObjectIdentifier instance, or null.
*/
public static ASN1ObjectIdentifier getInstance(
Object obj)
{
super(identifier);
if (obj == null || obj instanceof ASN1ObjectIdentifier)
{
return (ASN1ObjectIdentifier)obj;
}
if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier)
{
return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive();
}
if (obj instanceof byte[])
{
byte[] enc = (byte[])obj;
try
{
return (ASN1ObjectIdentifier)fromByteArray(enc);
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct object identifier from byte[]: " + e.getMessage());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
ASN1ObjectIdentifier(byte[] bytes)
/**
* return an Object Identifier from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @throws IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1ObjectIdentifier instance, or null.
*/
public static ASN1ObjectIdentifier getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
super(bytes);
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof ASN1ObjectIdentifier)
{
return getInstance(o);
}
else
{
return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets());
}
}
ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
ASN1ObjectIdentifier(
byte[] bytes)
{
super(oid, branch);
StringBuffer objId = new StringBuffer();
long value = 0;
BigInteger bigValue = null;
boolean first = true;
for (int i = 0; i != bytes.length; i++)
{
int b = bytes[i] & 0xff;
if (value <= LONG_LIMIT)
{
value += (b & 0x7f);
if ((b & 0x80) == 0) // end of number reached
{
if (first)
{
if (value < 40)
{
objId.append('0');
}
else if (value < 80)
{
objId.append('1');
value -= 40;
}
else
{
objId.append('2');
value -= 80;
}
first = false;
}
objId.append('.');
objId.append(value);
value = 0;
}
else
{
value <<= 7;
}
}
else
{
if (bigValue == null)
{
bigValue = BigInteger.valueOf(value);
}
bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
if ((b & 0x80) == 0)
{
if (first)
{
objId.append('2');
bigValue = bigValue.subtract(BigInteger.valueOf(80));
first = false;
}
objId.append('.');
objId.append(bigValue);
bigValue = null;
value = 0;
}
else
{
bigValue = bigValue.shiftLeft(7);
}
}
}
// BEGIN android-changed
/*
* Intern the identifier so there aren't hundreds of duplicates
* (in practice).
*/
this.identifier = objId.toString().intern();
// END android-changed
this.body = Arrays.clone(bytes);
}
/**
* Create an OID based on the passed in String.
*
* @param identifier a string representation of an OID.
*/
public ASN1ObjectIdentifier(
String identifier)
{
if (identifier == null)
{
throw new IllegalArgumentException("'identifier' cannot be null");
}
if (!isValidIdentifier(identifier))
{
throw new IllegalArgumentException("string " + identifier + " not an OID");
}
// BEGIN android-changed
/*
* Intern the identifier so there aren't hundreds of duplicates
* (in practice).
*/
this.identifier = identifier.intern();
// END android-changed
}
/**
* Create an OID that creates a branch under the current one.
*
* @param branchID node numbers for the new branch.
* @return the OID for the new created branch.
*/
ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branchID)
{
if (!isValidBranchID(branchID, 0))
{
throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
}
this.identifier = oid.getId() + "." + branchID;
}
/**
* Return the OID as a string.
*
* @return the string representation of the OID carried by this object.
*/
public String getId()
{
return identifier;
}
/**
@ -31,12 +229,268 @@ public class ASN1ObjectIdentifier
/**
* Return true if this oid is an extension of the passed in branch, stem.
*
* @param stem the arc or branch that is a possible parent.
* @return true if the branch is on the passed in stem, false otherwise.
* @return true if the branch is on the passed in stem, false otherwise.
*/
public boolean on(ASN1ObjectIdentifier stem)
{
String id = getId(), stemId = stem.getId();
return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId);
}
private void writeField(
ByteArrayOutputStream out,
long fieldValue)
{
byte[] result = new byte[9];
int pos = 8;
result[pos] = (byte)((int)fieldValue & 0x7f);
while (fieldValue >= (1L << 7))
{
fieldValue >>= 7;
result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
}
out.write(result, pos, 9 - pos);
}
private void writeField(
ByteArrayOutputStream out,
BigInteger fieldValue)
{
int byteCount = (fieldValue.bitLength() + 6) / 7;
if (byteCount == 0)
{
out.write(0);
}
else
{
BigInteger tmpValue = fieldValue;
byte[] tmp = new byte[byteCount];
for (int i = byteCount - 1; i >= 0; i--)
{
tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
tmpValue = tmpValue.shiftRight(7);
}
tmp[byteCount - 1] &= 0x7f;
out.write(tmp, 0, tmp.length);
}
}
private void doOutput(ByteArrayOutputStream aOut)
{
OIDTokenizer tok = new OIDTokenizer(identifier);
int first = Integer.parseInt(tok.nextToken()) * 40;
String secondToken = tok.nextToken();
if (secondToken.length() <= 18)
{
writeField(aOut, first + Long.parseLong(secondToken));
}
else
{
writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
}
while (tok.hasMoreTokens())
{
String token = tok.nextToken();
if (token.length() <= 18)
{
writeField(aOut, Long.parseLong(token));
}
else
{
writeField(aOut, new BigInteger(token));
}
}
}
private synchronized byte[] getBody()
{
if (body == null)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
doOutput(bOut);
body = bOut.toByteArray();
}
return body;
}
boolean isConstructed()
{
return false;
}
int encodedLength()
throws IOException
{
int length = getBody().length;
return 1 + StreamUtil.calculateBodyLength(length) + length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
byte[] enc = getBody();
out.write(BERTags.OBJECT_IDENTIFIER);
out.writeLength(enc.length);
out.write(enc);
}
public int hashCode()
{
return identifier.hashCode();
}
boolean asn1Equals(
ASN1Primitive o)
{
if (o == this)
{
return true;
}
if (!(o instanceof ASN1ObjectIdentifier))
{
return false;
}
return identifier.equals(((ASN1ObjectIdentifier)o).identifier);
}
public String toString()
{
return getId();
}
private static boolean isValidBranchID(
String branchID, int start)
{
boolean periodAllowed = false;
int pos = branchID.length();
while (--pos >= start)
{
char ch = branchID.charAt(pos);
// TODO Leading zeroes?
if ('0' <= ch && ch <= '9')
{
periodAllowed = true;
continue;
}
if (ch == '.')
{
if (!periodAllowed)
{
return false;
}
periodAllowed = false;
continue;
}
return false;
}
return periodAllowed;
}
private static boolean isValidIdentifier(
String identifier)
{
if (identifier.length() < 3 || identifier.charAt(1) != '.')
{
return false;
}
char first = identifier.charAt(0);
if (first < '0' || first > '2')
{
return false;
}
return isValidBranchID(identifier, 2);
}
/**
* Intern will return a reference to a pooled version of this object, unless it
* is not present in which case intern will add it.
* <p>
* The pool is also used by the ASN.1 parsers to limit the number of duplicated OID
* objects in circulation.
* </p>
* @return a reference to the identifier in the pool.
*/
public ASN1ObjectIdentifier intern()
{
synchronized (pool)
{
OidHandle hdl = new OidHandle(getBody());
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)pool.get(hdl);
if (oid != null)
{
return oid;
}
else
{
pool.put(hdl, this);
return this;
}
}
}
private static final Map pool = new HashMap();
private static class OidHandle
{
private int key;
private final byte[] enc;
OidHandle(byte[] enc)
{
this.key = Arrays.hashCode(enc);
this.enc = enc;
}
public int hashCode()
{
return key;
}
public boolean equals(Object o)
{
if (o instanceof OidHandle)
{
return Arrays.areEqual(enc, ((OidHandle)o).enc);
}
return false;
}
}
static ASN1ObjectIdentifier fromOctetString(byte[] enc)
{
OidHandle hdl = new OidHandle(enc);
synchronized (pool)
{
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)pool.get(hdl);
if (oid != null)
{
return oid;
}
}
return new ASN1ObjectIdentifier(enc);
}
}

@ -7,6 +7,96 @@ import java.io.InputStream;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
/**
* Abstract base for the ASN.1 OCTET STRING data type
* <p>
* This supports BER, and DER forms of the data.
* </p><p>
* DER form is always primitive single OCTET STRING, while
* BER support includes the constructed forms.
* </p>
* <hr>
* <p><b>X.690</b></p>
* <p><b>8: Basic encoding rules</b></p>
* <p><b>8.7 Encoding of an octetstring value</b></p>
* <p>
* <b>8.7.1</b> The encoding of an octetstring value shall be
* either primitive or constructed at the option of the sender.
* <blockquote>
* NOTE &mdash; Where it is necessary to transfer part of an octet string
* before the entire OCTET STRING is available, the constructed encoding
* is used.
* </blockquote>
* <p>
* <b>8.7.2</b> The primitive encoding contains zero,
* one or more contents octets equal in value to the octets
* in the data value, in the order they appear in the data value,
* and with the most significant bit of an octet of the data value
* aligned with the most significant bit of an octet of the contents octets.
* </p>
* <p>
* <b>8.7.3</b> The contents octets for the constructed encoding shall consist
* of zero, one, or more encodings.
* <blockquote>
* NOTE &mdash; Each such encoding includes identifier, length, and contents octets,
* and may include end-of-contents octets if it is constructed.
* </blockquote>
* </p>
* <p>
* <b>8.7.3.1</b> To encode an octetstring value in this way,
* it is segmented. Each segment shall consist of a series of
* consecutive octets of the value. There shall be no significance
* placed on the segment boundaries.
* <blockquote>
* NOTE &mdash; A segment may be of size zero, i.e. contain no octets.
* </blockquote>
* </p>
* <p>
* <b>8.7.3.2</b> Each encoding in the contents octets shall represent
* a segment of the overall octetstring, the encoding arising from
* a recursive application of this subclause.
* In this recursive application, each segment is treated as if it were
* a octetstring value. The encodings of the segments shall appear in the contents
* octets in the order in which their octets appear in the overall value.
* <blockquote>
* NOTE 1 &mdash; As a consequence of this recursion,
* each encoding in the contents octets may itself
* be primitive or constructed.
* However, such encodings will usually be primitive.
* <br />
* NOTE 2 &mdash; In particular, the tags in the contents octets are always universal class, number 4.
* </blockquote>
* </p>
* <p><b>9: Canonical encoding rules</b></p>
* <p><b>9.1 Length forms</b></p>
* <p>
* If the encoding is constructed, it shall employ the indefinite-length form.
* If the encoding is primitive, it shall include the fewest length octets necessary.
* [Contrast with 8.1.3.2 b).]
* </p>
* <p><b>9.2 String encoding forms</b></p>
* <p>
* BIT STRING, OCTET STRING,and restricted character string
* values shall be encoded with a primitive encoding if they would
* require no more than 1000 contents octets, and as a constructed
* encoding otherwise. The string fragments contained in
* the constructed encoding shall be encoded with a primitive encoding.
* The encoding of each fragment, except possibly
* the last, shall have 1000 contents octets. (Contrast with 8.21.6.)
* </p><p>
* <b>10: Distinguished encoding rules</b>
* </p><p>
* <b>10.1 Length forms</b>
* The definite form of length encoding shall be used,
* encoded in the minimum number of octets.
* [Contrast with 8.1.3.2 b).]
* </p><p>
* <b>10.2 String encoding forms</b>
* For BIT STRING, OCTET STRING and restricted character string types,
* the constructed form of encoding shall not be used.
* (Contrast with 8.21.6.)
* </p>
*/
public abstract class ASN1OctetString
extends ASN1Primitive
implements ASN1OctetStringParser
@ -76,6 +166,8 @@ public abstract class ASN1OctetString
}
/**
* Base constructor.
*
* @param string the octets making up the octet string.
*/
public ASN1OctetString(
@ -88,16 +180,31 @@ public abstract class ASN1OctetString
this.string = string;
}
/**
* Return the content of the OCTET STRING as an InputStream.
*
* @return an InputStream representing the OCTET STRING's content.
*/
public InputStream getOctetStream()
{
return new ByteArrayInputStream(string);
}
/**
* Return the parser associated with this object.
*
* @return a parser based on this OCTET STRING
*/
public ASN1OctetStringParser parser()
{
return this;
}
/**
* Return the content of the OCTET STRING as a byte array.
*
* @return the byte[] representing the OCTET STRING's content.
*/
public byte[] getOctets()
{
return string;

@ -2,8 +2,16 @@ package org.bouncycastle.asn1;
import java.io.InputStream;
/**
* A basic parser for an OCTET STRING object
*/
public interface ASN1OctetStringParser
extends ASN1Encodable, InMemoryRepresentable
{
/**
* Return the content of the OCTET STRING as an InputStream.
*
* @return an InputStream representing the OCTET STRING's content.
*/
public InputStream getOctetStream();
}

@ -2,6 +2,9 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Base class for ASN.1 primitive objects. These are the actual objects used to generate byte encodings.
*/
public abstract class ASN1Primitive
extends ASN1Object
{
@ -15,7 +18,7 @@ public abstract class ASN1Primitive
*
* @param data the byte stream to parse.
* @return the base ASN.1 object represented by the byte stream.
* @exception IOException if there is a problem parsing the data.
* @exception IOException if there is a problem parsing the data, or parsing the stream did not exhaust the available data.
*/
public static ASN1Primitive fromByteArray(byte[] data)
throws IOException
@ -24,7 +27,14 @@ public abstract class ASN1Primitive
try
{
return aIn.readObject();
ASN1Primitive o = aIn.readObject();
if (aIn.available() != 0)
{
throw new IOException("Extra data detected in stream");
}
return o;
}
catch (ClassCastException e)
{
@ -47,11 +57,21 @@ public abstract class ASN1Primitive
return this;
}
/**
* Return the current object as one which encodes using Distinguished Encoding Rules.
*
* @return a DER version of this.
*/
ASN1Primitive toDERObject()
{
return this;
}
/**
* Return the current object as one which encodes using Definite Length encoding.
*
* @return a DL version of this.
*/
ASN1Primitive toDLObject()
{
return this;

@ -2,18 +2,70 @@ package org.bouncycastle.asn1;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import org.bouncycastle.util.Arrays;
/**
* ASN.1 <code>SEQUENCE</code> and <code>SEQUENCE OF</code> constructs.
* <p>
* DER form is always definite form length fields, while
* BER support uses indefinite form.
* <hr>
* <p><b>X.690</b></p>
* <p><b>8: Basic encoding rules</b></p>
* <p><b>8.9 Encoding of a sequence value </b></p>
* 8.9.1 The encoding of a sequence value shall be constructed.
* <p>
* <b>8.9.2</b> The contents octets shall consist of the complete
* encoding of one data value from each of the types listed in
* the ASN.1 definition of the sequence type, in the order of
* their appearance in the definition, unless the type was referenced
* with the keyword <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
* </p><p>
* <b>8.9.3</b> The encoding of a data value may, but need not,
* be present for a type which was referenced with the keyword
* <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
* If present, it shall appear in the encoding at the point
* corresponding to the appearance of the type in the ASN.1 definition.
* </p><p>
* <b>8.10 Encoding of a sequence-of value </b>
* </p><p>
* <b>8.10.1</b> The encoding of a sequence-of value shall be constructed.
* <p>
* <b>8.10.2</b> The contents octets shall consist of zero,
* one or more complete encodings of data values from the type listed in
* the ASN.1 definition.
* <p>
* <b>8.10.3</b> The order of the encodings of the data values shall be
* the same as the order of the data values in the sequence-of value to
* be encoded.
* </p>
* <p><b>9: Canonical encoding rules</b></p>
* <p><b>9.1 Length forms</b></p>
* If the encoding is constructed, it shall employ the indefinite-length form.
* If the encoding is primitive, it shall include the fewest length octets necessary.
* [Contrast with 8.1.3.2 b).]
*
* <p><b>11: Restrictions on BER employed by both CER and DER</b></p>
* <p><b>11.5 Set and sequence components with default value</b></p>
* The encoding of a set value or sequence value shall not include
* an encoding for any component value which is equal to
* its default value.
*/
public abstract class ASN1Sequence
extends ASN1Primitive
implements org.bouncycastle.util.Iterable<ASN1Encodable>
{
protected Vector seq = new Vector();
/**
* return an ASN1Sequence from the given object.
* Return an ASN1Sequence from the given object.
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
* @return an ASN1Sequence instance, or null.
*/
public static ASN1Sequence getInstance(
Object obj)
@ -65,6 +117,7 @@ public abstract class ASN1Sequence
* false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1Sequence instance.
*/
public static ASN1Sequence getInstance(
ASN1TaggedObject obj,
@ -110,14 +163,15 @@ public abstract class ASN1Sequence
}
/**
* create an empty sequence
* Create an empty sequence
*/
protected ASN1Sequence()
{
}
/**
* create a sequence containing one object
* Create a sequence containing one object
* @param obj the object to be put in the SEQUENCE.
*/
protected ASN1Sequence(
ASN1Encodable obj)
@ -126,7 +180,8 @@ public abstract class ASN1Sequence
}
/**
* create a sequence containing a vector of objects.
* Create a sequence containing a vector of objects.
* @param v the vector of objects to be put in the SEQUENCE
*/
protected ASN1Sequence(
ASN1EncodableVector v)
@ -137,8 +192,8 @@ public abstract class ASN1Sequence
}
}
/**
* create a sequence containing a vector of objects.
/*
* Create a sequence containing a vector of objects.
*/
protected ASN1Sequence(
ASN1Encodable[] array)
@ -209,7 +264,7 @@ public abstract class ASN1Sequence
}
/**
* return the object at the sequence position indicated by index.
* Return the object at the sequence position indicated by index.
*
* @param index the sequence number (starting at zero) of the object
* @return the object at the sequence position indicated by index.
@ -221,7 +276,7 @@ public abstract class ASN1Sequence
}
/**
* return the number of objects in this sequence.
* Return the number of objects in this sequence.
*
* @return the number of objects in this sequence.
*/
@ -290,6 +345,10 @@ public abstract class ASN1Sequence
return encObj;
}
/**
* Change current SEQUENCE object to be encoded as {@link DERSequence}.
* This is part of Distinguished Encoding Rules form serialization.
*/
ASN1Primitive toDERObject()
{
ASN1Sequence derSeq = new DERSequence();
@ -299,6 +358,10 @@ public abstract class ASN1Sequence
return derSeq;
}
/**
* Change current SEQUENCE object to be encoded as {@link DLSequence}.
* This is part of Direct Length form serialization.
*/
ASN1Primitive toDLObject()
{
ASN1Sequence dlSeq = new DLSequence();
@ -320,4 +383,9 @@ public abstract class ASN1Sequence
{
return seq.toString();
}
public Iterator<ASN1Encodable> iterator()
{
return new Arrays.Iterator<ASN1Encodable>(toArray());
}
}

@ -2,9 +2,18 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* A basic parser for a SEQUENCE object
*/
public interface ASN1SequenceParser
extends ASN1Encodable, InMemoryRepresentable
{
/**
* Read the next object from the underlying object representing a SEQUENCE.
*
* @throws IOException for bad input stream.
* @return the next object, null if we are at the end.
*/
ASN1Encodable readObject()
throws IOException;
}

@ -1,12 +1,102 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
abstract public class ASN1Set
import org.bouncycastle.util.Arrays;
/**
* ASN.1 <code>SET</code> and <code>SET OF</code> constructs.
* <p>
* Note: This does not know which syntax the set is!
* (The difference: ordering of SET elements or not ordering.)
* <p>
* DER form is always definite form length fields, while
* BER support uses indefinite form.
* <p>
* The CER form support does not exist.
* <p>
* <hr>
* <h2>X.690</h2>
* <h3>8: Basic encoding rules</h3>
* <h4>8.11 Encoding of a set value </h4>
* <b>8.11.1</b> The encoding of a set value shall be constructed
* <p>
* <b>8.11.2</b> The contents octets shall consist of the complete
* encoding of a data value from each of the types listed in the
* ASN.1 definition of the set type, in an order chosen by the sender,
* unless the type was referenced with the keyword
* <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
* <p>
* <b>8.11.3</b> The encoding of a data value may, but need not,
* be present for a type which was referenced with the keyword
* <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
* <blockquote>
* NOTE &mdash; The order of data values in a set value is not significant,
* and places no constraints on the order during transfer
* </blockquote>
* <h4>8.12 Encoding of a set-of value</h4>
* <b>8.12.1</b> The encoding of a set-of value shall be constructed.
* <p>
* <b>8.12.2</b> The text of 8.10.2 applies:
* <i>The contents octets shall consist of zero,
* one or more complete encodings of data values from the type listed in
* the ASN.1 definition.</i>
* <p>
* <b>8.12.3</b> The order of data values need not be preserved by
* the encoding and subsequent decoding.
*
* <h3>9: Canonical encoding rules</h3>
* <h4>9.1 Length forms</h4>
* If the encoding is constructed, it shall employ the indefinite-length form.
* If the encoding is primitive, it shall include the fewest length octets necessary.
* [Contrast with 8.1.3.2 b).]
* <h4>9.3 Set components</h4>
* The encodings of the component values of a set value shall
* appear in an order determined by their tags as specified
* in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
* Additionally, for the purposes of determining the order in which
* components are encoded when one or more component is an untagged
* choice type, each untagged choice type is ordered as though it
* has a tag equal to that of the smallest tag in that choice type
* or any untagged choice types nested within.
*
* <h3>10: Distinguished encoding rules</h3>
* <h4>10.1 Length forms</h4>
* The definite form of length encoding shall be used,
* encoded in the minimum number of octets.
* [Contrast with 8.1.3.2 b).]
* <h4>10.3 Set components</h4>
* The encodings of the component values of a set value shall appear
* in an order determined by their tags as specified
* in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
* <blockquote>
* NOTE &mdash; Where a component of the set is an untagged choice type,
* the location of that component in the ordering will depend on
* the tag of the choice component being encoded.
* </blockquote>
*
* <h3>11: Restrictions on BER employed by both CER and DER</h3>
* <h4>11.5 Set and sequence components with default value </h4>
* The encoding of a set value or sequence value shall not include
* an encoding for any component value which is equal to
* its default value.
* <h4>11.6 Set-of components </h4>
* <p>
* The encodings of the component values of a set-of value
* shall appear in ascending order, the encodings being compared
* as octet strings with the shorter components being padded at
* their trailing end with 0-octets.
* <blockquote>
* NOTE &mdash; The padding octets are for comparison purposes only
* and do not appear in the encodings.
* </blockquote>
*/
public abstract class ASN1Set
extends ASN1Primitive
implements org.bouncycastle.util.Iterable<ASN1Encodable>
{
private Vector set = new Vector();
private boolean isSorted = false;
@ -16,6 +106,7 @@ abstract public class ASN1Set
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
* @return an ASN1Set instance, or null.
*/
public static ASN1Set getInstance(
Object obj)
@ -67,6 +158,7 @@ abstract public class ASN1Set
* false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1Set instance.
*/
public static ASN1Set getInstance(
ASN1TaggedObject obj,
@ -135,6 +227,7 @@ abstract public class ASN1Set
/**
* create a sequence containing one object
* @param obj object to be added to the SET.
*/
protected ASN1Set(
ASN1Encodable obj)
@ -144,6 +237,8 @@ abstract public class ASN1Set
/**
* create a sequence containing a vector of objects.
* @param v a vector of objects to make up the SET.
* @param doSort true if should be sorted DER style, false otherwise.
*/
protected ASN1Set(
ASN1EncodableVector v,
@ -160,7 +255,7 @@ abstract public class ASN1Set
}
}
/**
/*
* create a sequence containing a vector of objects.
*/
protected ASN1Set(
@ -275,6 +370,10 @@ abstract public class ASN1Set
return hashCode;
}
/**
* Change current SET object to be encoded as {@link DERSet}.
* This is part of Distinguished Encoding Rules form serialization.
*/
ASN1Primitive toDERObject()
{
if (isSorted)
@ -304,6 +403,10 @@ abstract public class ASN1Set
}
}
/**
* Change current SET object to be encoded as {@link DLSet}.
* This is part of Direct Length form serialization.
*/
ASN1Primitive toDLObject()
{
ASN1Set derSet = new DLSet();
@ -381,22 +484,17 @@ abstract public class ASN1Set
return len == a.length;
}
private byte[] getEncoded(
private byte[] getDEREncoded(
ASN1Encodable obj)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
try
{
aOut.writeObject(obj);
return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
}
catch (IOException e)
{
throw new IllegalArgumentException("cannot encode object added to SET");
}
return bOut.toByteArray();
}
protected void sort()
@ -413,13 +511,13 @@ abstract public class ASN1Set
{
int index = 0;
int swapIndex = 0;
byte[] a = getEncoded((ASN1Encodable)set.elementAt(0));
byte[] a = getDEREncoded((ASN1Encodable)set.elementAt(0));
swapped = false;
while (index != lastSwap)
{
byte[] b = getEncoded((ASN1Encodable)set.elementAt(index + 1));
byte[] b = getDEREncoded((ASN1Encodable)set.elementAt(index + 1));
if (lessThanOrEqual(a, b))
{
@ -457,4 +555,9 @@ abstract public class ASN1Set
{
return set.toString();
}
public Iterator<ASN1Encodable> iterator()
{
return new Arrays.Iterator<ASN1Encodable>(toArray());
}
}

@ -2,9 +2,18 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* A basic parser for a SET object
*/
public interface ASN1SetParser
extends ASN1Encodable, InMemoryRepresentable
{
/**
* Read the next object from the underlying object representing a SET.
*
* @throws IOException for bad input stream.
* @return the next object, null if we are at the end.
*/
public ASN1Encodable readObject()
throws IOException;
}

@ -4,6 +4,9 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* A parser for ASN.1 streams which also returns, where possible, parsers for the objects it encounters.
*/
public class ASN1StreamParser
{
private final InputStream _in;
@ -58,7 +61,7 @@ public class ASN1StreamParser
{
if (!constructed)
{
throw new IOException("indefinite length primitive encoding encountered");
throw new IOException("indefinite-length primitive encoding encountered");
}
return readIndef(tag);
@ -89,8 +92,7 @@ public class ASN1StreamParser
}
}
// TODO ASN1Exception
throw new RuntimeException("implicit tagging not implemented");
throw new ASN1Exception("implicit tagging not implemented");
}
ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
@ -142,11 +144,11 @@ public class ASN1StreamParser
//
int length = ASN1InputStream.readLength(_in, _limit);
if (length < 0) // indefinite length method
if (length < 0) // indefinite-length method
{
if (!isConstructed)
{
throw new IOException("indefinite length primitive encoding encountered");
throw new IOException("indefinite-length primitive encoding encountered");
}
IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);

@ -1,6 +1,13 @@
package org.bouncycastle.asn1;
/**
* General interface implemented by ASN.1 STRING objects.
*/
public interface ASN1String
{
/**
* Return a Java String representation of this STRING type's content.
* @return a Java String representation of this STRING.
*/
public String getString();
}

@ -192,6 +192,7 @@ public abstract class ASN1TaggedObject
public ASN1Encodable getObjectParser(
int tag,
boolean isExplicit)
throws IOException
{
switch (tag)
{
@ -208,7 +209,7 @@ public abstract class ASN1TaggedObject
return getObject();
}
throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
throw new ASN1Exception("implicit tagging not implemented for tag: " + tag);
}
public ASN1Primitive getLoadedObject()

@ -1,22 +1,328 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.SimpleTimeZone;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
/**
- * UTC time object.
* Internal facade of {@link ASN1UTCTime}.
* <p>
* This datatype is valid only from 1950-01-01 00:00:00 UTC until 2049-12-31 23:59:59 UTC.
* <p>
* <hr>
* <p><b>X.690</b></p>
* <p><b>11: Restrictions on BER employed by both CER and DER</b></p>
* <p><b>11.8 UTCTime </b></p>
* <b>11.8.1</b> The encoding shall terminate with "Z",
* as described in the ITU-T X.680 | ISO/IEC 8824-1 clause on UTCTime.
* <p>
* <b>11.8.2</b> The seconds element shall always be present.
* <p>
* <b>11.8.3</b> Midnight (GMT) shall be represented in the form:
* <blockquote>
* "YYMMDD000000Z"
* </blockquote>
* where "YYMMDD" represents the day following the midnight in question.
*/
public class ASN1UTCTime
extends DERUTCTime
extends ASN1Primitive
{
ASN1UTCTime(byte[] bytes)
private byte[] time;
/**
* return an UTC Time from the passed in object.
*
* @param obj an ASN1UTCTime or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return an ASN1UTCTime instance, or null.
*/
public static ASN1UTCTime getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1UTCTime)
{
return (ASN1UTCTime)obj;
}
if (obj instanceof byte[])
{
try
{
return (ASN1UTCTime)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an UTC Time from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return an ASN1UTCTime instance, or null.
*/
public static ASN1UTCTime getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Object o = obj.getObject();
if (explicit || o instanceof ASN1UTCTime)
{
return getInstance(o);
}
else
{
return new ASN1UTCTime(((ASN1OctetString)o).getOctets());
}
}
/**
* The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
* never encoded. When you're creating one of these objects from scratch, that's
* what you want to use, otherwise we'll try to deal with whatever gets read from
* the input stream... (this is why the input format is different from the getTime()
* method output).
* <p>
*
* @param time the time string.
*/
public ASN1UTCTime(
String time)
{
this.time = Strings.toByteArray(time);
try
{
this.getDate();
}
catch (ParseException e)
{
throw new IllegalArgumentException("invalid date string: " + e.getMessage());
}
}
/**
* base constructor from a java.util.date object
* @param time the Date to build the time from.
*/
public ASN1UTCTime(
Date time)
{
// BEGIN android-changed
// Was: SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'");
SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'", Locale.US);
// END android-changed
dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
this.time = Strings.toByteArray(dateF.format(time));
}
/**
* Base constructor from a java.util.date and Locale - you may need to use this if the default locale
* doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
*
* @param time a date object representing the time of interest.
* @param locale an appropriate Locale for producing an ASN.1 UTCTime value.
*/
public ASN1UTCTime(
Date time,
Locale locale)
{
// BEGIN android-changed
// Was: SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'", locale);
SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'", Locale.US);
dateF.setCalendar(Calendar.getInstance(locale));
// END android-changed
dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
this.time = Strings.toByteArray(dateF.format(time));
}
ASN1UTCTime(
byte[] time)
{
this.time = time;
}
/**
* return the time as a date based on whatever a 2 digit year will return. For
* standardised processing use getAdjustedDate().
*
* @return the resulting date
* @exception ParseException if the date string cannot be parsed.
*/
public Date getDate()
throws ParseException
{
// BEGIN android-changed
// Was: SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz", Locale.US);
// END android-changed
return dateF.parse(getTime());
}
/**
* return the time as an adjusted date
* in the range of 1950 - 2049.
*
* @return a date in the range of 1950 to 2049.
* @exception ParseException if the date string cannot be parsed.
*/
public Date getAdjustedDate()
throws ParseException
{
// BEGIN android-changed
// Was: SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz", Locale.US);
// END android-changed
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
return dateF.parse(getAdjustedTime());
}
/**
* return the time - always in the form of
* YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
* <p>
* Normally in a certificate we would expect "Z" rather than "GMT",
* however adding the "GMT" means we can just use:
* <pre>
* dateF = new SimpleDateFormat("yyMMddHHmmssz");
* </pre>
* To read in the time and get a date which is compatible with our local
* time zone.
* <p>
* <b>Note:</b> In some cases, due to the local date processing, this
* may lead to unexpected results. If you want to stick the normal
* convention of 1950 to 2049 use the getAdjustedTime() method.
*/
public String getTime()
{
String stime = Strings.fromByteArray(time);
//
// standardise the format.
//
if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0)
{
if (stime.length() == 11)
{
return stime.substring(0, 10) + "00GMT+00:00";
}
else
{
return stime.substring(0, 12) + "GMT+00:00";
}
}
else
{
int index = stime.indexOf('-');
if (index < 0)
{
index = stime.indexOf('+');
}
String d = stime;
if (index == stime.length() - 3)
{
d += "00";
}
if (index == 10)
{
return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15);
}
else
{
return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17);
}
}
}
/**
* return a time string as an adjusted date with a 4 digit year. This goes
* in the range of 1950 - 2049.
*/
public String getAdjustedTime()
{
String d = this.getTime();
if (d.charAt(0) < '5')
{
return "20" + d;
}
else
{
return "19" + d;
}
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
int length = time.length;
return 1 + StreamUtil.calculateBodyLength(length) + length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
super(bytes);
out.write(BERTags.UTC_TIME);
int length = time.length;
out.writeLength(length);
for (int i = 0; i != length; i++)
{
out.write((byte)time[i]);
}
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1UTCTime))
{
return false;
}
return Arrays.areEqual(time, ((ASN1UTCTime)o).time);
}
public ASN1UTCTime(Date time)
public int hashCode()
{
super(time);
return Arrays.hashCode(time);
}
public ASN1UTCTime(String time)
public String toString()
{
super(time);
return Strings.fromByteArray(time);
}
}

@ -1,10 +1,114 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* An indefinite-length encoding version of an application specific object.
*/
public class BERApplicationSpecific
extends DERApplicationSpecific
extends ASN1ApplicationSpecific
{
BERApplicationSpecific(
boolean isConstructed,
int tag,
byte[] octets)
{
super(isConstructed, tag, octets);
}
/**
* Create an application specific object with a tagging of explicit/constructed.
*
* @param tag the tag number for this object.
* @param object the object to be contained.
*/
public BERApplicationSpecific(
int tag,
ASN1Encodable object)
throws IOException
{
this(true, tag, object);
}
/**
* Create an application specific object with the tagging style given by the value of constructed.
*
* @param constructed true if the object is constructed.
* @param tag the tag number for this object.
* @param object the object to be contained.
*/
public BERApplicationSpecific(
boolean constructed,
int tag,
ASN1Encodable object)
throws IOException
{
super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
}
private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
throws IOException
{
byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.BER);
if (explicit)
{
return data;
}
else
{
int lenBytes = getLengthOfHeader(data);
byte[] tmp = new byte[data.length - lenBytes];
System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
return tmp;
}
}
/**
* Create an application specific object which is marked as constructed
*
* @param tagNo the tag number for this object.
* @param vec the objects making up the application specific object.
*/
public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
{
super(tagNo, vec);
super(true, tagNo, getEncodedVector(vec));
}
private static byte[] getEncodedVector(ASN1EncodableVector vec)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
for (int i = 0; i != vec.size(); i++)
{
try
{
bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.BER));
}
catch (IOException e)
{
throw new ASN1ParsingException("malformed object: " + e, e);
}
}
return bOut.toByteArray();
}
/* (non-Javadoc)
* @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
*/
void encode(ASN1OutputStream out) throws IOException
{
int classBits = BERTags.APPLICATION;
if (isConstructed)
{
classBits |= BERTags.CONSTRUCTED;
}
out.writeTag(classBits, tag);
out.write(0x80);
out.write(octets);
out.write(0x00);
out.write(0x00);
}
}

@ -2,6 +2,9 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* A parser for indefinite-length application specific objects.
*/
public class BERApplicationSpecificParser
implements ASN1ApplicationSpecificParser
{
@ -14,18 +17,34 @@ public class BERApplicationSpecificParser
this.parser = parser;
}
/**
* Return the object contained in this application specific object,
* @return the contained object.
* @throws IOException if the underlying stream cannot be read, or does not contain an ASN.1 encoding.
*/
public ASN1Encodable readObject()
throws IOException
{
return parser.readObject();
}
/**
* Return an in-memory, encodable, representation of the application specific object.
*
* @return a BERApplicationSpecific.
* @throws IOException if there is an issue loading the data.
*/
public ASN1Primitive getLoadedObject()
throws IOException
{
return new BERApplicationSpecific(tag, parser.readVector());
}
/**
* Return a BERApplicationSpecific representing this parser and its contents.
*
* @return a BERApplicationSpecific
*/
public ASN1Primitive toASN1Primitive()
{
try
@ -37,5 +56,4 @@ public class BERApplicationSpecificParser
throw new ASN1ParsingException(e.getMessage(), e);
}
}
}

@ -1,23 +1,25 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Base class for generators for indefinite-length structures.
*/
public class BERGenerator
extends ASN1Generator
{
private boolean _tagged = false;
private boolean _isExplicit;
private int _tagNo;
protected BERGenerator(
OutputStream out)
{
super(out);
}
public BERGenerator(
protected BERGenerator(
OutputStream out,
int tagNo,
boolean isExplicit)
@ -72,18 +74,6 @@ public class BERGenerator
writeHdr(tag);
}
}
protected void writeBERBody(
InputStream contentStream)
throws IOException
{
int ch;
while ((ch = contentStream.read()) >= 0)
{
_out.write(ch);
}
}
protected void writeBEREnd()
throws IOException

@ -3,18 +3,21 @@ package org.bouncycastle.asn1;
import java.io.IOException;
import java.util.Enumeration;
/**
* Carrier class for an indefinite-length SEQUENCE.
*/
public class BERSequence
extends ASN1Sequence
{
/**
* create an empty sequence
* Create an empty sequence
*/
public BERSequence()
{
}
/**
* create a sequence containing one object
* Create a sequence containing one object
*/
public BERSequence(
ASN1Encodable obj)
@ -23,7 +26,7 @@ public class BERSequence
}
/**
* create a sequence containing a vector of objects.
* Create a sequence containing a vector of objects.
*/
public BERSequence(
ASN1EncodableVector v)
@ -32,7 +35,7 @@ public class BERSequence
}
/**
* create a sequence containing an array of objects.
* Create a sequence containing an array of objects.
*/
public BERSequence(
ASN1Encodable[] array)
@ -52,8 +55,6 @@ public class BERSequence
return 2 + length + 2;
}
/*
*/
void encode(
ASN1OutputStream out)
throws IOException

@ -2,6 +2,9 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Parser for indefinite-length SEQUENCEs.
*/
public class BERSequenceParser
implements ASN1SequenceParser
{
@ -12,18 +15,35 @@ public class BERSequenceParser
this._parser = parser;
}
/**
* Read the next object in the SEQUENCE.
*
* @return the next object in the SEQUENCE, null if there are no more.
* @throws IOException if there is an issue reading the underlying stream.
*/
public ASN1Encodable readObject()
throws IOException
{
return _parser.readObject();
}
/**
* Return an in-memory, encodable, representation of the SEQUENCE.
*
* @return a BERSequence.
* @throws IOException if there is an issue loading the data.
*/
public ASN1Primitive getLoadedObject()
throws IOException
{
return new BERSequence(_parser.readVector());
}
/**
* Return an BERSequence representing this parser and its contents.
*
* @return an BERSequence
*/
public ASN1Primitive toASN1Primitive()
{
try

@ -3,17 +3,22 @@ package org.bouncycastle.asn1;
import java.io.IOException;
import java.util.Enumeration;
/**
* Carrier class for an indefinite-length SET.
*/
public class BERSet
extends ASN1Set
{
/**
* create an empty sequence
* Create an empty SET.
*/
public BERSet()
{
}
/**
* Create a SET containing one object.
*
* @param obj - a single object that makes up the set.
*/
public BERSet(
@ -23,7 +28,8 @@ public class BERSet
}
/**
* @param v - a vector of objects making up the set.
* Create a SET containing multiple objects.
* @param v a vector of objects making up the set.
*/
public BERSet(
ASN1EncodableVector v)
@ -32,7 +38,8 @@ public class BERSet
}
/**
* create a set from an array of objects.
* Create a SET from an array of objects.
* @param a an array of ASN.1 objects.
*/
public BERSet(
ASN1Encodable[] a)
@ -52,8 +59,6 @@ public class BERSet
return 2 + length + 2;
}
/*
*/
void encode(
ASN1OutputStream out)
throws IOException
@ -70,4 +75,4 @@ public class BERSet
out.write(0x00);
out.write(0x00);
}
}
}

@ -2,6 +2,9 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Parser for indefinite-length SETs.
*/
public class BERSetParser
implements ASN1SetParser
{
@ -12,18 +15,35 @@ public class BERSetParser
this._parser = parser;
}
/**
* Read the next object in the SET.
*
* @return the next object in the SET, null if there are no more.
* @throws IOException if there is an issue reading the underlying stream.
*/
public ASN1Encodable readObject()
throws IOException
{
return _parser.readObject();
}
/**
* Return an in-memory, encodable, representation of the SET.
*
* @return a BERSet.
* @throws IOException if there is an issue loading the data.
*/
public ASN1Primitive getLoadedObject()
throws IOException
{
return new BERSet(_parser.readVector());
}
/**
* Return an BERSet representing this parser and its contents.
*
* @return an BERSet
*/
public ASN1Primitive toASN1Primitive()
{
try
@ -35,4 +55,4 @@ public class BERSetParser
throw new ASN1ParsingException(e.getMessage(), e);
}
}
}
}

@ -2,6 +2,9 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Parser for indefinite-length tagged objects.
*/
public class BERTaggedObjectParser
implements ASN1TaggedObjectParser
{
@ -19,16 +22,34 @@ public class BERTaggedObjectParser
_parser = parser;
}
/**
* Return true if this tagged object is marked as constructed.
*
* @return true if constructed, false otherwise.
*/
public boolean isConstructed()
{
return _constructed;
}
/**
* Return the tag number associated with this object.
*
* @return the tag number.
*/
public int getTagNo()
{
return _tagNumber;
}
/**
* Return an object parser for the contents of this tagged object.
*
* @param tag the actual tag number of the object (needed if implicit).
* @param isExplicit true if the contained object was explicitly tagged, false if implicit.
* @return an ASN.1 encodable object parser.
* @throws IOException if there is an issue building the object parser from the stream.
*/
public ASN1Encodable getObjectParser(
int tag,
boolean isExplicit)
@ -46,12 +67,23 @@ public class BERTaggedObjectParser
return _parser.readImplicit(_constructed, tag);
}
/**
* Return an in-memory, encodable, representation of the tagged object.
*
* @return an ASN1TaggedObject.
* @throws IOException if there is an issue loading the data.
*/
public ASN1Primitive getLoadedObject()
throws IOException
{
return _parser.readTaggedObject(_constructed, _tagNumber);
}
/**
* Return an ASN1TaggedObject representing this parser and its contents.
*
* @return an ASN1TaggedObject
*/
public ASN1Primitive toASN1Primitive()
{
try
@ -63,4 +95,4 @@ public class BERTaggedObjectParser
throw new ASN1ParsingException(e.getMessage());
}
}
}
}

@ -3,28 +3,27 @@ package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.bouncycastle.util.Arrays;
/**
* Base class for an application specific object
* A DER encoding version of an application specific object.
*/
public class DERApplicationSpecific
extends ASN1Primitive
extends ASN1ApplicationSpecific
{
private final boolean isConstructed;
private final int tag;
private final byte[] octets;
DERApplicationSpecific(
boolean isConstructed,
int tag,
byte[] octets)
{
this.isConstructed = isConstructed;
this.tag = tag;
this.octets = octets;
super(isConstructed, tag, octets);
}
/**
* Create an application specific object from the passed in data. This will assume
* the data does not represent a constructed object.
*
* @param tag the tag number for this object.
* @param octets the encoding of the object's body.
*/
public DERApplicationSpecific(
int tag,
byte[] octets)
@ -32,44 +31,67 @@ public class DERApplicationSpecific
this(false, tag, octets);
}
/**
* Create an application specific object with a tagging of explicit/constructed.
*
* @param tag the tag number for this object.
* @param object the object to be contained.
*/
public DERApplicationSpecific(
int tag,
int tag,
ASN1Encodable object)
throws IOException
{
this(true, tag, object);
}
/**
* Create an application specific object with the tagging style given by the value of constructed.
*
* @param constructed true if the object is constructed.
* @param tag the tag number for this object.
* @param object the object to be contained.
*/
public DERApplicationSpecific(
boolean explicit,
boolean constructed,
int tag,
ASN1Encodable object)
throws IOException
{
ASN1Primitive primitive = object.toASN1Primitive();
byte[] data = primitive.getEncoded(ASN1Encoding.DER);
super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
}
this.isConstructed = explicit || (primitive instanceof ASN1Set || primitive instanceof ASN1Sequence);
this.tag = tag;
private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
throws IOException
{
byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DER);
if (explicit)
{
this.octets = data;
return data;
}
else
{
int lenBytes = getLengthOfHeader(data);
byte[] tmp = new byte[data.length - lenBytes];
System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
this.octets = tmp;
return tmp;
}
}
/**
* Create an application specific object which is marked as constructed
*
* @param tagNo the tag number for this object.
* @param vec the objects making up the application specific object.
*/
public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
{
this.tag = tagNo;
this.isConstructed = true;
super(true, tagNo, getEncodedVector(vec));
}
private static byte[] getEncodedVector(ASN1EncodableVector vec)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
for (int i = 0; i != vec.size(); i++)
@ -83,121 +105,7 @@ public class DERApplicationSpecific
throw new ASN1ParsingException("malformed object: " + e, e);
}
}
this.octets = bOut.toByteArray();
}
public static DERApplicationSpecific getInstance(Object obj)
{
if (obj == null || obj instanceof DERApplicationSpecific)
{
return (DERApplicationSpecific)obj;
}
else if (obj instanceof byte[])
{
try
{
return DERApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct object from byte[]: " + e.getMessage());
}
}
else if (obj instanceof ASN1Encodable)
{
ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
if (primitive instanceof ASN1Sequence)
{
return (DERApplicationSpecific)primitive;
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
private int getLengthOfHeader(byte[] data)
{
int length = data[1] & 0xff; // TODO: assumes 1 byte tag
if (length == 0x80)
{
return 2; // indefinite-length encoding
}
if (length > 127)
{
int size = length & 0x7f;
// Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
if (size > 4)
{
throw new IllegalStateException("DER length more than 4 bytes: " + size);
}
return size + 2;
}
return 2;
}
public boolean isConstructed()
{
return isConstructed;
}
public byte[] getContents()
{
return octets;
}
public int getApplicationTag()
{
return tag;
}
/**
* Return the enclosed object assuming explicit tagging.
*
* @return the resulting object
* @throws IOException if reconstruction fails.
*/
public ASN1Primitive getObject()
throws IOException
{
return new ASN1InputStream(getContents()).readObject();
}
/**
* Return the enclosed object assuming implicit tagging.
*
* @param derTagNo the type tag that should be applied to the object's contents.
* @return the resulting object
* @throws IOException if reconstruction fails.
*/
public ASN1Primitive getObject(int derTagNo)
throws IOException
{
if (derTagNo >= 0x1f)
{
throw new IOException("unsupported tag number");
}
byte[] orig = this.getEncoded();
byte[] tmp = replaceTagNumber(derTagNo, orig);
if ((orig[0] & BERTags.CONSTRUCTED) != 0)
{
tmp[0] |= BERTags.CONSTRUCTED;
}
return new ASN1InputStream(tmp).readObject();
}
int encodedLength()
throws IOException
{
return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
return bOut.toByteArray();
}
/* (non-Javadoc)
@ -213,64 +121,4 @@ public class DERApplicationSpecific
out.writeEncoded(classBits, tag, octets);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DERApplicationSpecific))
{
return false;
}
DERApplicationSpecific other = (DERApplicationSpecific)o;
return isConstructed == other.isConstructed
&& tag == other.tag
&& Arrays.areEqual(octets, other.octets);
}
public int hashCode()
{
return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
}
private byte[] replaceTagNumber(int newTag, byte[] input)
throws IOException
{
int tagNo = input[0] & 0x1f;
int index = 1;
//
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
//
if (tagNo == 0x1f)
{
tagNo = 0;
int b = input[index++] & 0xff;
// X.690-0207 8.1.2.4.2
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
if ((b & 0x7f) == 0) // Note: -1 will pass
{
throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
}
while ((b >= 0) && ((b & 0x80) != 0))
{
tagNo |= (b & 0x7f);
tagNo <<= 7;
b = input[index++] & 0xff;
}
tagNo |= (b & 0x7f);
}
byte[] tmp = new byte[input.length - index + 1];
System.arraycopy(input, index, tmp, 1, tmp.length - 1);
tmp[0] = (byte)newTag;
return tmp;
}
}

@ -5,19 +5,20 @@ import java.io.IOException;
import org.bouncycastle.util.Arrays;
/**
* DER BMPString object.
* Carrier class for DER encoding BMPString object.
*/
public class DERBMPString
extends ASN1Primitive
implements ASN1String
{
private char[] string;
private final char[] string;
/**
* return a BMP String from the given object.
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
* @return a DERBMPString instance, or null.
*/
public static DERBMPString getInstance(
Object obj)
@ -50,6 +51,7 @@ public class DERBMPString
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return a DERBMPString instance.
*/
public static DERBMPString getInstance(
ASN1TaggedObject obj,
@ -69,6 +71,7 @@ public class DERBMPString
/**
* basic constructor - byte encoded string.
* @param string the encoded BMP STRING to wrap.
*/
DERBMPString(
byte[] string)
@ -90,6 +93,7 @@ public class DERBMPString
/**
* basic constructor
* @param string a String to wrap as a BMP STRING.
*/
public DERBMPString(
String string)

@ -1,99 +1,19 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.Streams;
/**
* A BIT STRING with DER encoding.
*/
public class DERBitString
extends ASN1Primitive
implements ASN1String
extends ASN1BitString
{
private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
protected byte[] data;
protected int padBits;
/**
* return the correct number of pad bits for a bit string defined in
* a 32 bit constant
*/
static protected int getPadBits(
int bitString)
{
int val = 0;
for (int i = 3; i >= 0; i--)
{
//
// this may look a little odd, but if it isn't done like this pre jdk1.2
// JVM's break!
//
if (i != 0)
{
if ((bitString >> (i * 8)) != 0)
{
val = (bitString >> (i * 8)) & 0xFF;
break;
}
}
else
{
if (bitString != 0)
{
val = bitString & 0xFF;
break;
}
}
}
if (val == 0)
{
return 7;
}
int bits = 1;
while (((val <<= 1) & 0xFF) != 0)
{
bits++;
}
return 8 - bits;
}
/**
* return the correct number of bytes for a bit string defined in
* a 32 bit constant
*/
static protected byte[] getBytes(int bitString)
{
int bytes = 4;
for (int i = 3; i >= 1; i--)
{
if ((bitString & (0xFF << (i * 8))) != 0)
{
break;
}
bytes--;
}
byte[] result = new byte[bytes];
for (int i = 0; i < bytes; i++)
{
result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
}
return result;
}
/**
* return a Bit String from the passed in object
*
* @param obj a DERBitString or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return a DERBitString instance, or null.
*/
public static DERBitString getInstance(
Object obj)
@ -102,6 +22,10 @@ public class DERBitString
{
return (DERBitString)obj;
}
if (obj instanceof DLBitString)
{
return new DERBitString(((DLBitString)obj).data, ((DLBitString)obj).padBits);
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
@ -114,6 +38,7 @@ public class DERBitString
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return a DERBitString instance, or null.
*/
public static DERBitString getInstance(
ASN1TaggedObject obj,
@ -135,9 +60,16 @@ public class DERBitString
byte data,
int padBits)
{
this.data = new byte[1];
this.data[0] = data;
this.padBits = padBits;
this(toByteArray(data), padBits);
}
private static byte[] toByteArray(byte data)
{
byte[] rv = new byte[1];
rv[0] = data;
return rv;
}
/**
@ -148,8 +80,7 @@ public class DERBitString
byte[] data,
int padBits)
{
this.data = data;
this.padBits = padBits;
super(data, padBits);
}
public DERBitString(
@ -161,42 +92,14 @@ public class DERBitString
public DERBitString(
int value)
{
this.data = getBytes(value);
this.padBits = getPadBits(value);
super(getBytes(value), getPadBits(value));
}
public DERBitString(
ASN1Encodable obj)
throws IOException
{
this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
this.padBits = 0;
}
public byte[] getBytes()
{
return data;
}
public int getPadBits()
{
return padBits;
}
/**
* @return the value of the bit string as an int (truncating if necessary)
*/
public int intValue()
{
int value = 0;
for (int i = 0; i != data.length && i != 4; i++)
{
value |= (data[i] & 0xff) << (8 * i);
}
return value;
super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
}
boolean isConstructed()
@ -213,64 +116,15 @@ public class DERBitString
ASN1OutputStream out)
throws IOException
{
byte[] bytes = new byte[getBytes().length + 1];
byte[] string = derForm(data, padBits);
byte[] bytes = new byte[string.length + 1];
bytes[0] = (byte)getPadBits();
System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
System.arraycopy(string, 0, bytes, 1, bytes.length - 1);
out.writeEncoded(BERTags.BIT_STRING, bytes);
}
public int hashCode()
{
return padBits ^ Arrays.hashCode(data);
}
protected boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DERBitString))
{
return false;
}
DERBitString other = (DERBitString)o;
return this.padBits == other.padBits
&& Arrays.areEqual(this.data, other.data);
}
public String getString()
{
StringBuffer buf = new StringBuffer("#");
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
try
{
aOut.writeObject(this);
}
catch (IOException e)
{
throw new RuntimeException("internal error encoding BitString");
}
byte[] string = bOut.toByteArray();
for (int i = 0; i != string.length; i++)
{
buf.append(table[(string[i] >>> 4) & 0xf]);
buf.append(table[string[i] & 0xf]);
}
return buf.toString();
}
public String toString()
{
return getString();
}
static DERBitString fromOctetString(byte[] bytes)
{
if (bytes.length < 1)
@ -288,26 +142,4 @@ public class DERBitString
return new DERBitString(data, padBits);
}
static DERBitString fromInputStream(int length, InputStream stream)
throws IOException
{
if (length < 1)
{
throw new IllegalArgumentException("truncated BIT STRING detected");
}
int padBits = stream.read();
byte[] data = new byte[length - 1];
if (data.length != 0)
{
if (Streams.readFully(stream, data) != data.length)
{
throw new EOFException("EOF encountered in middle of BIT STRING");
}
}
return new DERBitString(data, padBits);
}
}

@ -1,196 +1,22 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import org.bouncycastle.util.Arrays;
/**
* @deprecated use ASN1Boolean
*/
public class DERBoolean
extends ASN1Primitive
extends ASN1Boolean
{
private static final byte[] TRUE_VALUE = new byte[] { (byte)0xff };
private static final byte[] FALSE_VALUE = new byte[] { 0 };
// BEGIN android-changed
final private byte[] value;
// END android-changed
public static final ASN1Boolean FALSE = new ASN1Boolean(false);
public static final ASN1Boolean TRUE = new ASN1Boolean(true);
/**
* return a boolean from the passed in object.
*
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1Boolean getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Boolean)
{
return (ASN1Boolean)obj;
}
if (obj instanceof DERBoolean)
{
return ((DERBoolean)obj).isTrue() ? DERBoolean.TRUE : DERBoolean.FALSE;
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return a ASN1Boolean from the passed in boolean.
*/
public static ASN1Boolean getInstance(
boolean value)
{
return (value ? TRUE : FALSE);
}
/**
* return a ASN1Boolean from the passed in boolean.
*/
public static ASN1Boolean getInstance(
int value)
{
return (value != 0 ? TRUE : FALSE);
}
// BEGIN android-added
/**
* return a DERBoolean from the passed in array.
*/
public static DERBoolean getInstance(
byte[] octets)
{
return (octets[0] != 0) ? TRUE : FALSE;
}
// END android-added
/**
* return a Boolean from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1Boolean getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof DERBoolean)
{
return getInstance(o);
}
else
{
return ASN1Boolean.fromOctetString(((ASN1OctetString)o).getOctets());
}
}
// BEGIN android-changed
protected DERBoolean(
// END android-changed
byte[] value)
{
if (value.length != 1)
{
throw new IllegalArgumentException("byte value should have 1 byte in it");
}
if (value[0] == 0)
{
this.value = FALSE_VALUE;
}
else if (value[0] == 0xff)
{
this.value = TRUE_VALUE;
}
else
{
this.value = Arrays.clone(value);
}
}
/**
* @deprecated use getInstance(boolean) method.
* @param value
*/
// BEGIN android-changed
protected DERBoolean(
boolean value)
// END android-changed
public DERBoolean(boolean value)
{
this.value = (value) ? TRUE_VALUE : FALSE_VALUE;
super(value);
}
public boolean isTrue()
DERBoolean(byte[] value)
{
return (value[0] != 0);
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
return 3;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.BOOLEAN, value);
}
protected boolean asn1Equals(
ASN1Primitive o)
{
if ((o == null) || !(o instanceof DERBoolean))
{
return false;
}
return (value[0] == ((DERBoolean)o).value[0]);
}
public int hashCode()
{
return value[0];
}
public String toString()
{
return (value[0] != 0) ? "TRUE" : "FALSE";
}
static ASN1Boolean fromOctetString(byte[] value)
{
if (value.length != 1)
{
throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
}
if (value[0] == 0)
{
return FALSE;
}
else if (value[0] == 0xff)
{
return TRUE;
}
else
{
return new ASN1Boolean(value);
}
super(value);
}
}

@ -2,7 +2,7 @@ package org.bouncycastle.asn1;
/**
* a general class for building up a vector of DER encodable objects -
* this will eventually be superceded by ASN1EncodableVector so you should
* this will eventually be superseded by ASN1EncodableVector so you should
* use that class in preference.
*/
public class DEREncodableVector

@ -1,170 +1,37 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.util.Arrays;
/**
* Use ASN1Enumerated instead of this.
* @deprecated Use ASN1Enumerated instead of this.
*/
public class DEREnumerated
extends ASN1Primitive
extends ASN1Enumerated
{
byte[] bytes;
/**
* return an integer from the passed in object
*
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1Enumerated getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Enumerated)
{
return (ASN1Enumerated)obj;
}
if (obj instanceof DEREnumerated)
{
return new ASN1Enumerated(((DEREnumerated)obj).getValue());
}
if (obj instanceof byte[])
{
try
{
return (ASN1Enumerated)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an Enumerated from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1Enumerated getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof DEREnumerated)
{
return getInstance(o);
}
else
{
return fromOctetString(((ASN1OctetString)o).getOctets());
}
}
/**
* @param bytes the value of this enumerated as an encoded BigInteger (signed).
* @deprecated use ASN1Enumerated
*/
public DEREnumerated(
int value)
DEREnumerated(byte[] bytes)
{
bytes = BigInteger.valueOf(value).toByteArray();
super(bytes);
}
/**
* @param value the value of this enumerated.
* @deprecated use ASN1Enumerated
*/
public DEREnumerated(
BigInteger value)
public DEREnumerated(BigInteger value)
{
bytes = value.toByteArray();
super(value);
}
/**
* @param value the value of this enumerated.
* @deprecated use ASN1Enumerated
*/
public DEREnumerated(
byte[] bytes)
{
this.bytes = bytes;
}
public BigInteger getValue()
{
return new BigInteger(bytes);
}
boolean isConstructed()
public DEREnumerated(int value)
{
return false;
}
int encodedLength()
{
return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.ENUMERATED, bytes);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DEREnumerated))
{
return false;
}
DEREnumerated other = (DEREnumerated)o;
return Arrays.areEqual(this.bytes, other.bytes);
}
public int hashCode()
{
return Arrays.hashCode(bytes);
}
private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
static ASN1Enumerated fromOctetString(byte[] enc)
{
if (enc.length > 1)
{
return new ASN1Enumerated(Arrays.clone(enc));
}
if (enc.length == 0)
{
throw new IllegalArgumentException("ENUMERATED has zero length");
}
int value = enc[0] & 0xff;
if (value >= cache.length)
{
return new ASN1Enumerated(Arrays.clone(enc));
}
ASN1Enumerated possibleMatch = cache[value];
if (possibleMatch == null)
{
possibleMatch = cache[value] = new ASN1Enumerated(Arrays.clone(enc));
}
return possibleMatch;
super(value);
}
}

@ -32,7 +32,7 @@ public class DERExternal
offset++;
enc = getObjFromVector(vector, offset);
}
if (!(enc instanceof DERTaggedObject))
if (!(enc instanceof ASN1TaggedObject))
{
dataValueDescriptor = (ASN1Primitive) enc;
offset++;
@ -44,11 +44,11 @@ public class DERExternal
throw new IllegalArgumentException("input vector too large");
}
if (!(enc instanceof DERTaggedObject))
if (!(enc instanceof ASN1TaggedObject))
{
throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
}
DERTaggedObject obj = (DERTaggedObject)enc;
ASN1TaggedObject obj = (ASN1TaggedObject)enc;
setEncoding(obj.getTagNo());
externalContent = obj.getObject();
}

@ -2,13 +2,18 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Parser DER EXTERNAL tagged objects.
*/
public class DERExternalParser
implements ASN1Encodable, InMemoryRepresentable
{
private ASN1StreamParser _parser;
/**
*
* Base constructor.
*
* @param parser the underlying parser to read the DER EXTERNAL from.
*/
public DERExternalParser(ASN1StreamParser parser)
{
@ -21,6 +26,12 @@ public class DERExternalParser
return _parser.readObject();
}
/**
* Return an in-memory, encodable, representation of the EXTERNAL object.
*
* @return a DERExternal.
* @throws IOException if there is an issue loading the data.
*/
public ASN1Primitive getLoadedObject()
throws IOException
{
@ -33,20 +44,25 @@ public class DERExternalParser
throw new ASN1Exception(e.getMessage(), e);
}
}
/**
* Return an DERExternal representing this parser and its contents.
*
* @return an DERExternal
*/
public ASN1Primitive toASN1Primitive()
{
try
try
{
return getLoadedObject();
}
catch (IOException ioe)
catch (IOException ioe)
{
throw new ASN1ParsingException("unable to get DER object", ioe);
}
catch (IllegalArgumentException ioe)
catch (IllegalArgumentException ioe)
{
throw new ASN1ParsingException("unable to get DER object", ioe);
}
}
}
}

@ -5,12 +5,22 @@ import java.io.IOException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
/**
* Carrier class for a DER encoding GeneralString
*/
public class DERGeneralString
extends ASN1Primitive
implements ASN1String
{
private byte[] string;
private final byte[] string;
/**
* return a GeneralString from the given object.
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
* @return a DERBMPString instance, or null.
*/
public static DERGeneralString getInstance(
Object obj)
{
@ -35,6 +45,16 @@ public class DERGeneralString
+ obj.getClass().getName());
}
/**
* return a GeneralString from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return a DERGeneralString instance.
*/
public static DERGeneralString getInstance(
ASN1TaggedObject obj,
boolean explicit)
@ -56,11 +76,21 @@ public class DERGeneralString
this.string = string;
}
/**
* Construct a GeneralString from the passed in String.
*
* @param string the string to be contained in this object.
*/
public DERGeneralString(String string)
{
this.string = Strings.toByteArray(string);
}
/**
* Return a Java String representation of our contained String.
*
* @return a Java String representing our contents.
*/
public String getString()
{
return Strings.fromByteArray(string);
@ -71,6 +101,11 @@ public class DERGeneralString
return getString();
}
/**
* Return a byte array representation of our contained String.
*
* @return a byte array representing our contents.
*/
public byte[] getOctets()
{
return Arrays.clone(string);

@ -1,350 +1,28 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
/**
* Generalized time object.
* DER Generalized time object.
*/
public class DERGeneralizedTime
extends ASN1Primitive
extends ASN1GeneralizedTime
{
private byte[] time;
/**
* return a generalized time from the passed in object
*
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1GeneralizedTime getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1GeneralizedTime)
{
return (ASN1GeneralizedTime)obj;
}
if (obj instanceof DERGeneralizedTime)
{
return new ASN1GeneralizedTime(((DERGeneralizedTime)obj).time);
}
if (obj instanceof byte[])
{
try
{
return (ASN1GeneralizedTime)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return a Generalized Time object from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1GeneralizedTime getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof DERGeneralizedTime)
{
return getInstance(o);
}
else
{
return new ASN1GeneralizedTime(((ASN1OctetString)o).getOctets());
}
}
/**
* The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
* for local time, or Z+-HHMM on the end, for difference between local
* time and UTC time. The fractional second amount f must consist of at
* least one number with trailing zeroes removed.
*
* @param time the time string.
* @exception IllegalArgumentException if String is an illegal format.
*/
public DERGeneralizedTime(
String time)
{
this.time = Strings.toByteArray(time);
try
{
this.getDate();
}
catch (ParseException e)
{
throw new IllegalArgumentException("invalid date string: " + e.getMessage());
}
}
/**
* base constructor from a java.util.date object
*/
public DERGeneralizedTime(
Date time)
{
SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
this.time = Strings.toByteArray(dateF.format(time));
}
DERGeneralizedTime(
byte[] bytes)
{
this.time = bytes;
}
/**
* Return the time.
* @return The time string as it appeared in the encoded object.
*/
public String getTimeString()
{
return Strings.fromByteArray(time);
}
/**
* return the time - always in the form of
* YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
* <p>
* Normally in a certificate we would expect "Z" rather than "GMT",
* however adding the "GMT" means we can just use:
* <pre>
* dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
* </pre>
* To read in the time and get a date which is compatible with our local
* time zone.
*/
public String getTime()
{
String stime = Strings.fromByteArray(time);
//
// standardise the format.
//
if (stime.charAt(stime.length() - 1) == 'Z')
{
return stime.substring(0, stime.length() - 1) + "GMT+00:00";
}
else
{
int signPos = stime.length() - 5;
char sign = stime.charAt(signPos);
if (sign == '-' || sign == '+')
{
return stime.substring(0, signPos)
+ "GMT"
+ stime.substring(signPos, signPos + 3)
+ ":"
+ stime.substring(signPos + 3);
}
else
{
signPos = stime.length() - 3;
sign = stime.charAt(signPos);
if (sign == '-' || sign == '+')
{
return stime.substring(0, signPos)
+ "GMT"
+ stime.substring(signPos)
+ ":00";
}
}
}
return stime + calculateGMTOffset();
}
private String calculateGMTOffset()
DERGeneralizedTime(byte[] bytes)
{
String sign = "+";
TimeZone timeZone = TimeZone.getDefault();
int offset = timeZone.getRawOffset();
if (offset < 0)
{
sign = "-";
offset = -offset;
}
int hours = offset / (60 * 60 * 1000);
int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000);
try
{
if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate()))
{
hours += sign.equals("+") ? 1 : -1;
}
}
catch (ParseException e)
{
// we'll do our best and ignore daylight savings
}
return "GMT" + sign + convert(hours) + ":" + convert(minutes);
super(bytes);
}
private String convert(int time)
public DERGeneralizedTime(Date time)
{
if (time < 10)
{
return "0" + time;
}
return Integer.toString(time);
}
public Date getDate()
throws ParseException
{
SimpleDateFormat dateF;
String stime = Strings.fromByteArray(time);
String d = stime;
if (stime.endsWith("Z"))
{
if (hasFractionalSeconds())
{
dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
}
else
{
dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
}
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
}
else if (stime.indexOf('-') > 0 || stime.indexOf('+') > 0)
{
d = this.getTime();
if (hasFractionalSeconds())
{
dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz");
}
else
{
dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
}
dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
}
else
{
if (hasFractionalSeconds())
{
dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
}
else
{
dateF = new SimpleDateFormat("yyyyMMddHHmmss");
}
dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
}
if (hasFractionalSeconds())
{
// java misinterprets extra digits as being milliseconds...
String frac = d.substring(14);
int index;
for (index = 1; index < frac.length(); index++)
{
char ch = frac.charAt(index);
if (!('0' <= ch && ch <= '9'))
{
break;
}
}
if (index - 1 > 3)
{
frac = frac.substring(0, 4) + frac.substring(index);
d = d.substring(0, 14) + frac;
}
else if (index - 1 == 1)
{
frac = frac.substring(0, index) + "00" + frac.substring(index);
d = d.substring(0, 14) + frac;
}
else if (index - 1 == 2)
{
frac = frac.substring(0, index) + "0" + frac.substring(index);
d = d.substring(0, 14) + frac;
}
}
return dateF.parse(d);
super(time);
}
private boolean hasFractionalSeconds()
public DERGeneralizedTime(String time)
{
for (int i = 0; i != time.length; i++)
{
if (time[i] == '.')
{
if (i == 14)
{
return true;
}
}
}
return false;
super(time);
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
int length = time.length;
return 1 + StreamUtil.calculateBodyLength(length) + length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.GENERALIZED_TIME, time);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DERGeneralizedTime))
{
return false;
}
return Arrays.areEqual(time, ((DERGeneralizedTime)o).time);
}
public int hashCode()
{
return Arrays.hashCode(time);
}
// TODO: create proper DER encoding.
}

@ -0,0 +1,124 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
public class DERGraphicString
extends ASN1Primitive
implements ASN1String
{
private final byte[] string;
/**
* return a Graphic String from the passed in object
*
* @param obj a DERGraphicString or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return a DERGraphicString instance, or null.
*/
public static DERGraphicString getInstance(
Object obj)
{
if (obj == null || obj instanceof DERGraphicString)
{
return (DERGraphicString)obj;
}
if (obj instanceof byte[])
{
try
{
return (DERGraphicString)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return a Graphic String from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return a DERGraphicString instance, or null.
*/
public static DERGraphicString getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof DERGraphicString)
{
return getInstance(o);
}
else
{
return new DERGraphicString(((ASN1OctetString)o).getOctets());
}
}
/**
* basic constructor - with bytes.
* @param string the byte encoding of the characters making up the string.
*/
public DERGraphicString(
byte[] string)
{
this.string = Arrays.clone(string);
}
public byte[] getOctets()
{
return Arrays.clone(string);
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.GRAPHIC_STRING, string);
}
public int hashCode()
{
return Arrays.hashCode(string);
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DERGraphicString))
{
return false;
}
DERGraphicString s = (DERGraphicString)o;
return Arrays.areEqual(string, s.string);
}
public String getString()
{
return Strings.fromByteArray(string);
}
}

@ -12,12 +12,14 @@ public class DERIA5String
extends ASN1Primitive
implements ASN1String
{
private byte[] string;
private final byte[] string;
/**
* return a IA5 string from the passed in object
*
* @param obj a DERIA5String or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return a DERIA5String instance, or null.
*/
public static DERIA5String getInstance(
Object obj)
@ -50,6 +52,7 @@ public class DERIA5String
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return a DERIA5String instance, or null.
*/
public static DERIA5String getInstance(
ASN1TaggedObject obj,
@ -69,6 +72,7 @@ public class DERIA5String
/**
* basic constructor - with bytes.
* @param string the byte encoding of the characters making up the string.
*/
DERIA5String(
byte[] string)
@ -78,6 +82,7 @@ public class DERIA5String
/**
* basic constructor - without validation.
* @param string the base string to use..
*/
public DERIA5String(
String string)
@ -163,7 +168,8 @@ public class DERIA5String
* return true if the passed in String can be represented without
* loss as an IA5String, false otherwise.
*
* @return true if in printable set, false otherwise.
* @param str the string to check.
* @return true if character set in IA5String set, false otherwise.
*/
public static boolean isIA5String(
String str)

@ -1,160 +1,30 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.util.Arrays;
/**
* Use ASN1Integer instead of this,
* @deprecated Use ASN1Integer instead of this,
*/
public class DERInteger
extends ASN1Primitive
extends ASN1Integer
{
byte[] bytes;
/**
* return an integer from the passed in object
*
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1Integer getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Integer)
{
return (ASN1Integer)obj;
}
if (obj instanceof DERInteger)
{
return new ASN1Integer((((DERInteger)obj).getValue()));
}
if (obj instanceof byte[])
{
try
{
return (ASN1Integer)fromByteArray((byte[])obj);
}
catch (Exception e)
{
throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an Integer from a tagged object.
* Constructor from a byte array containing a signed representation of the number.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1Integer getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof DERInteger)
{
return getInstance(o);
}
else
{
return new ASN1Integer(ASN1OctetString.getInstance(obj.getObject()).getOctets());
}
}
/**
* @deprecated use ASN1Integer constructor
*/
public DERInteger(
long value)
{
bytes = BigInteger.valueOf(value).toByteArray();
}
/**
* @deprecated use ASN1Integer constructor
* @param bytes a byte array containing the signed number.A copy is made of the byte array.
*/
public DERInteger(
BigInteger value)
public DERInteger(byte[] bytes)
{
bytes = value.toByteArray();
super(bytes, true);
}
/**
* @deprecated use ASN1Integer constructor
*/
public DERInteger(
byte[] bytes)
public DERInteger(BigInteger value)
{
this.bytes = bytes;
}
public BigInteger getValue()
{
return new BigInteger(bytes);
}
/**
* in some cases positive values get crammed into a space,
* that's not quite big enough...
*/
public BigInteger getPositiveValue()
{
return new BigInteger(1, bytes);
}
boolean isConstructed()
{
return false;
}
int encodedLength()
{
return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
out.writeEncoded(BERTags.INTEGER, bytes);
}
public int hashCode()
{
int value = 0;
for (int i = 0; i != bytes.length; i++)
{
value ^= (bytes[i] & 0xff) << (i % 4);
}
return value;
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DERInteger))
{
return false;
}
DERInteger other = (DERInteger)o;
return Arrays.areEqual(bytes, other.bytes);
super(value);
}
public String toString()
public DERInteger(long value)
{
return getValue().toString();
super(value);
}
}

@ -12,12 +12,14 @@ public class DERNumericString
extends ASN1Primitive
implements ASN1String
{
private byte[] string;
private final byte[] string;
/**
* return a Numeric string from the passed in object
*
* @param obj a DERNumericString or an object that can be converted into one.
* @exception IllegalArgumentException if the object cannot be converted.
* @return a DERNumericString instance, or null
*/
public static DERNumericString getInstance(
Object obj)
@ -50,6 +52,7 @@ public class DERNumericString
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
* @return a DERNumericString instance, or null.
*/
public static DERNumericString getInstance(
ASN1TaggedObject obj,

@ -1,458 +1,24 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.util.Arrays;
/**
* Use ASN1ObjectIdentifier instead of this,
*
* @deprecated Use ASN1ObjectIdentifier instead of this,
*/
public class DERObjectIdentifier
extends ASN1Primitive
extends ASN1ObjectIdentifier
{
String identifier;
private byte[] body;
/**
* return an OID from the passed in object
*
* @throws IllegalArgumentException if the object cannot be converted.
*/
public static ASN1ObjectIdentifier getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1ObjectIdentifier)
{
return (ASN1ObjectIdentifier)obj;
}
if (obj instanceof DERObjectIdentifier)
{
return new ASN1ObjectIdentifier(((DERObjectIdentifier)obj).getId());
}
if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier)
{
return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive();
}
if (obj instanceof byte[])
{
byte[] enc = (byte[])obj;
if (enc[0] == BERTags.OBJECT_IDENTIFIER)
{
try
{
return (ASN1ObjectIdentifier)fromByteArray(enc);
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
}
}
else
{ // TODO: this really shouldn't be supported here...
return ASN1ObjectIdentifier.fromOctetString((byte[])obj);
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* return an Object Identifier from a tagged object.
*
* @param obj the tagged object holding the object we want
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @throws IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1ObjectIdentifier getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof DERObjectIdentifier)
{
return getInstance(o);
}
else
{
return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets());
}
}
private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
DERObjectIdentifier(
byte[] bytes)
public DERObjectIdentifier(String identifier)
{
StringBuffer objId = new StringBuffer();
long value = 0;
BigInteger bigValue = null;
boolean first = true;
for (int i = 0; i != bytes.length; i++)
{
int b = bytes[i] & 0xff;
if (value <= LONG_LIMIT)
{
value += (b & 0x7f);
if ((b & 0x80) == 0) // end of number reached
{
if (first)
{
if (value < 40)
{
objId.append('0');
}
else if (value < 80)
{
objId.append('1');
value -= 40;
}
else
{
objId.append('2');
value -= 80;
}
first = false;
}
objId.append('.');
objId.append(value);
value = 0;
}
else
{
value <<= 7;
}
}
else
{
if (bigValue == null)
{
bigValue = BigInteger.valueOf(value);
}
bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
if ((b & 0x80) == 0)
{
if (first)
{
objId.append('2');
bigValue = bigValue.subtract(BigInteger.valueOf(80));
first = false;
}
objId.append('.');
objId.append(bigValue);
bigValue = null;
value = 0;
}
else
{
bigValue = bigValue.shiftLeft(7);
}
}
}
// BEGIN android-changed
/*
* Intern the identifier so there aren't hundreds of duplicates
* (in practice).
*/
this.identifier = objId.toString().intern();
// END android-changed
this.body = Arrays.clone(bytes);
super(identifier);
}
/**
* @deprecated use ASN1ObjectIdentifier constructor.
*/
public DERObjectIdentifier(
String identifier)
DERObjectIdentifier(byte[] bytes)
{
if (identifier == null)
{
throw new IllegalArgumentException("'identifier' cannot be null");
}
if (!isValidIdentifier(identifier))
{
throw new IllegalArgumentException("string " + identifier + " not an OID");
}
// BEGIN android-changed
/*
* Intern the identifier so there aren't hundreds of duplicates
* (in practice).
*/
this.identifier = identifier.intern();
// END android-changed
super(bytes);
}
DERObjectIdentifier(DERObjectIdentifier oid, String branchID)
DERObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
{
if (!isValidBranchID(branchID, 0))
{
throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
}
this.identifier = oid.getId() + "." + branchID;
}
public String getId()
{
return identifier;
}
private void writeField(
ByteArrayOutputStream out,
long fieldValue)
{
byte[] result = new byte[9];
int pos = 8;
result[pos] = (byte)((int)fieldValue & 0x7f);
while (fieldValue >= (1L << 7))
{
fieldValue >>= 7;
result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
}
out.write(result, pos, 9 - pos);
}
private void writeField(
ByteArrayOutputStream out,
BigInteger fieldValue)
{
int byteCount = (fieldValue.bitLength() + 6) / 7;
if (byteCount == 0)
{
out.write(0);
}
else
{
BigInteger tmpValue = fieldValue;
byte[] tmp = new byte[byteCount];
for (int i = byteCount - 1; i >= 0; i--)
{
tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
tmpValue = tmpValue.shiftRight(7);
}
tmp[byteCount - 1] &= 0x7f;
out.write(tmp, 0, tmp.length);
}
}
private void doOutput(ByteArrayOutputStream aOut)
{
OIDTokenizer tok = new OIDTokenizer(identifier);
int first = Integer.parseInt(tok.nextToken()) * 40;
String secondToken = tok.nextToken();
if (secondToken.length() <= 18)
{
writeField(aOut, first + Long.parseLong(secondToken));
}
else
{
writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
}
while (tok.hasMoreTokens())
{
String token = tok.nextToken();
if (token.length() <= 18)
{
writeField(aOut, Long.parseLong(token));
}
else
{
writeField(aOut, new BigInteger(token));
}
}
}
protected synchronized byte[] getBody()
{
if (body == null)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
doOutput(bOut);
body = bOut.toByteArray();
}
return body;
}
boolean isConstructed()
{
return false;
}
int encodedLength()
throws IOException
{
int length = getBody().length;
return 1 + StreamUtil.calculateBodyLength(length) + length;
}
void encode(
ASN1OutputStream out)
throws IOException
{
byte[] enc = getBody();
out.write(BERTags.OBJECT_IDENTIFIER);
out.writeLength(enc.length);
out.write(enc);
}
public int hashCode()
{
return identifier.hashCode();
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof DERObjectIdentifier))
{
return false;
}
return identifier.equals(((DERObjectIdentifier)o).identifier);
}
public String toString()
{
return getId();
}
private static boolean isValidBranchID(
String branchID, int start)
{
boolean periodAllowed = false;
int pos = branchID.length();
while (--pos >= start)
{
char ch = branchID.charAt(pos);
// TODO Leading zeroes?
if ('0' <= ch && ch <= '9')
{
periodAllowed = true;
continue;
}
if (ch == '.')
{
if (!periodAllowed)
{
return false;
}
periodAllowed = false;
continue;
}
return false;
}
return periodAllowed;
}
private static boolean isValidIdentifier(
String identifier)
{
if (identifier.length() < 3 || identifier.charAt(1) != '.')
{
return false;
}
char first = identifier.charAt(0);
if (first < '0' || first > '2')
{
return false;
}
return isValidBranchID(identifier, 2);
}
private static ASN1ObjectIdentifier[][] cache = new ASN1ObjectIdentifier[256][];
static ASN1ObjectIdentifier fromOctetString(byte[] enc)
{
if (enc.length < 3)
{
return new ASN1ObjectIdentifier(enc);
}
int idx1 = enc[enc.length - 2] & 0xff;
// in this case top bit is always zero
int idx2 = enc[enc.length - 1] & 0x7f;
ASN1ObjectIdentifier possibleMatch;
synchronized (cache)
{
ASN1ObjectIdentifier[] first = cache[idx1];
if (first == null)
{
first = cache[idx1] = new ASN1ObjectIdentifier[128];
}
possibleMatch = first[idx2];
if (possibleMatch == null)
{
return first[idx2] = new ASN1ObjectIdentifier(enc);
}
if (Arrays.areEqual(enc, possibleMatch.getBody()))
{
return possibleMatch;
}
idx1 = (idx1 + 1) & 0xff;
first = cache[idx1];
if (first == null)
{
first = cache[idx1] = new ASN1ObjectIdentifier[128];
}
possibleMatch = first[idx2];
if (possibleMatch == null)
{
return first[idx2] = new ASN1ObjectIdentifier(enc);
}
if (Arrays.areEqual(enc, possibleMatch.getBody()))
{
return possibleMatch;
}
idx2 = (idx2 + 1) & 0x7f;
possibleMatch = first[idx2];
if (possibleMatch == null)
{
return first[idx2] = new ASN1ObjectIdentifier(enc);
}
}
if (Arrays.areEqual(enc, possibleMatch.getBody()))
{
return possibleMatch;
}
return new ASN1ObjectIdentifier(enc);
super(oid, branch);
}
}

@ -2,10 +2,15 @@ package org.bouncycastle.asn1;
import java.io.IOException;
/**
* Carrier class for a DER encoding OCTET STRING
*/
public class DEROctetString
extends ASN1OctetString
{
/**
* Base constructor.
*
* @param string the octets making up the octet string.
*/
public DEROctetString(
@ -14,6 +19,11 @@ public class DEROctetString
super(string);
}
/**
* Constructor from the encoding of an ASN.1 object.
*
* @param obj the object to be encoded.
*/
public DEROctetString(
ASN1Encodable obj)
throws IOException

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save