diff options
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/dvcs')
21 files changed, 1236 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/dvcs/CCPDRequestBuilder.java b/pkix/src/main/java/org/spongycastle/dvcs/CCPDRequestBuilder.java new file mode 100644 index 00000000..e48dd2a3 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/CCPDRequestBuilder.java @@ -0,0 +1,32 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.dvcs.DVCSRequestInformationBuilder; +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.asn1.dvcs.ServiceType; + +/** + * Builder of CCPD requests (Certify Claim of Possession of Data). + */ +public class CCPDRequestBuilder + extends DVCSRequestBuilder +{ + public CCPDRequestBuilder() + { + super(new DVCSRequestInformationBuilder(ServiceType.CCPD)); + } + + /** + * Builds CCPD request. + * + * @param messageImprint - the message imprint to include. + * @return + * @throws DVCSException + */ + public DVCSRequest build(MessageImprint messageImprint) + throws DVCSException + { + Data data = new Data(messageImprint.toASN1Structure()); + + return createDVCRequest(data); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/CCPDRequestData.java b/pkix/src/main/java/org/spongycastle/dvcs/CCPDRequestData.java new file mode 100644 index 00000000..202dd774 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/CCPDRequestData.java @@ -0,0 +1,48 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.dvcs.Data; + +/** + * Data piece of DVCRequest for CCPD service (Certify Claim of Possession of Data). + * It contains CCPD-specific selector interface. + * <p/> + * This objects are constructed internally, + * to build DVCS request to CCPD service use CCPDRequestBuilder. + */ +public class CCPDRequestData + extends DVCSRequestData +{ + /** + * Construct from corresponding ASN.1 Data structure. + * Note, that data should have messageImprint choice, + * otherwise DVCSConstructionException is thrown. + * + * @param data + * @throws DVCSConstructionException + */ + CCPDRequestData(Data data) + throws DVCSConstructionException + { + super(data); + initDigest(); + } + + private void initDigest() + throws DVCSConstructionException + { + if (data.getMessageImprint() == null) + { + throw new DVCSConstructionException("DVCSRequest.data.messageImprint should be specified for CCPD service"); + } + } + + /** + * Get MessageImprint value + * + * @return + */ + public MessageImprint getMessageImprint() + { + return new MessageImprint(data.getMessageImprint()); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/CPDRequestBuilder.java b/pkix/src/main/java/org/spongycastle/dvcs/CPDRequestBuilder.java new file mode 100644 index 00000000..0b0b8189 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/CPDRequestBuilder.java @@ -0,0 +1,34 @@ +package org.spongycastle.dvcs; + +import java.io.IOException; + +import org.spongycastle.asn1.dvcs.DVCSRequestInformationBuilder; +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.asn1.dvcs.ServiceType; + +/** + * Builder of DVCSRequests to CPD service (Certify Possession of Data). + */ +public class CPDRequestBuilder + extends DVCSRequestBuilder +{ + public CPDRequestBuilder() + { + super(new DVCSRequestInformationBuilder(ServiceType.CPD)); + } + + /** + * Build CPD request. + * + * @param messageBytes - data to be certified + * @return + * @throws DVCSException + */ + public DVCSRequest build(byte[] messageBytes) + throws DVCSException, IOException + { + Data data = new Data(messageBytes); + + return createDVCRequest(data); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/CPDRequestData.java b/pkix/src/main/java/org/spongycastle/dvcs/CPDRequestData.java new file mode 100644 index 00000000..a7c1f5a8 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/CPDRequestData.java @@ -0,0 +1,40 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.dvcs.Data; + +/** + * Data piece of DVCRequest for CPD service (Certify Possession of Data). + * It contains CPD-specific selector interface. + * <p/> + * This objects are constructed internally, + * to build DVCS request to CPD service use CPDRequestBuilder. + */ +public class CPDRequestData + extends DVCSRequestData +{ + CPDRequestData(Data data) + throws DVCSConstructionException + { + super(data); + initMessage(); + } + + private void initMessage() + throws DVCSConstructionException + { + if (data.getMessage() == null) + { + throw new DVCSConstructionException("DVCSRequest.data.message should be specified for CPD service"); + } + } + + /** + * Get contained message (data to be certified). + * + * @return + */ + public byte[] getMessage() + { + return data.getMessage().getOctets(); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSConstructionException.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSConstructionException.java new file mode 100644 index 00000000..73e6b0dc --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSConstructionException.java @@ -0,0 +1,20 @@ +package org.spongycastle.dvcs; + +/** + * Exception thrown when failed to initialize some DVCS-related staff. + */ +public class DVCSConstructionException + extends DVCSException +{ + private static final long serialVersionUID = 660035299653583980L; + + public DVCSConstructionException(String message) + { + super(message); + } + + public DVCSConstructionException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSException.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSException.java new file mode 100644 index 00000000..5aa8f51e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSException.java @@ -0,0 +1,28 @@ +package org.spongycastle.dvcs; + +/** + * General DVCSException. + */ +public class DVCSException + extends Exception +{ + private static final long serialVersionUID = 389345256020131488L; + + private Throwable cause; + + public DVCSException(String message) + { + super(message); + } + + public DVCSException(String message, Throwable cause) + { + super(message); + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSMessage.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSMessage.java new file mode 100644 index 00000000..6758f359 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSMessage.java @@ -0,0 +1,22 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cms.ContentInfo; + +public abstract class DVCSMessage +{ + private final ContentInfo contentInfo; + + protected DVCSMessage(ContentInfo contentInfo) + { + this.contentInfo = contentInfo; + } + + public ASN1ObjectIdentifier getContentType() + { + return contentInfo.getContentType(); + } + + public abstract ASN1Encodable getContent(); +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSParsingException.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSParsingException.java new file mode 100644 index 00000000..e1452576 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSParsingException.java @@ -0,0 +1,20 @@ +package org.spongycastle.dvcs; + +/** + * DVCS parsing exception - thrown when failed to parse DVCS message. + */ +public class DVCSParsingException + extends DVCSException +{ + private static final long serialVersionUID = -7895880961377691266L; + + public DVCSParsingException(String message) + { + super(message); + } + + public DVCSParsingException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequest.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequest.java new file mode 100644 index 00000000..f8658ab5 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequest.java @@ -0,0 +1,134 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.cms.ContentInfo; +import org.spongycastle.asn1.cms.SignedData; +import org.spongycastle.asn1.dvcs.DVCSObjectIdentifiers; +import org.spongycastle.asn1.dvcs.ServiceType; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.cms.CMSSignedData; + +/** + * DVCRequest is general request to DVCS (RFC 3029). + * It represents requests for all types of services. + * Requests for different services differ in DVCData structure. + */ +public class DVCSRequest + extends DVCSMessage +{ + private org.spongycastle.asn1.dvcs.DVCSRequest asn1; + private DVCSRequestInfo reqInfo; + private DVCSRequestData data; + + /** + * Constructs DVCRequest from CMS SignedData object. + * + * @param signedData the CMS SignedData object containing the request + * @throws DVCSConstructionException + */ + public DVCSRequest(CMSSignedData signedData) + throws DVCSConstructionException + { + this(SignedData.getInstance(signedData.toASN1Structure().getContent()).getEncapContentInfo()); + } + + /** + * Construct a DVCS Request from a ContentInfo + * + * @param contentInfo the contentInfo representing the DVCSRequest + * @throws DVCSConstructionException + */ + public DVCSRequest(ContentInfo contentInfo) + throws DVCSConstructionException + { + super(contentInfo); + + if (!DVCSObjectIdentifiers.id_ct_DVCSRequestData.equals(contentInfo.getContentType())) + { + throw new DVCSConstructionException("ContentInfo not a DVCS Request"); + } + + try + { + if (contentInfo.getContent().toASN1Primitive() instanceof ASN1Sequence) + { + this.asn1 = org.spongycastle.asn1.dvcs.DVCSRequest.getInstance(contentInfo.getContent()); + } + else + { + this.asn1 = org.spongycastle.asn1.dvcs.DVCSRequest.getInstance(ASN1OctetString.getInstance(contentInfo.getContent()).getOctets()); + } + } + catch (Exception e) + { + throw new DVCSConstructionException("Unable to parse content: " + e.getMessage(), e); + } + + this.reqInfo = new DVCSRequestInfo(asn1.getRequestInformation()); + + int service = reqInfo.getServiceType(); + if (service == ServiceType.CPD.getValue().intValue()) + { + this.data = new CPDRequestData(asn1.getData()); + } + else if (service == ServiceType.VSD.getValue().intValue()) + { + this.data = new VSDRequestData(asn1.getData()); + } + else if (service == ServiceType.VPKC.getValue().intValue()) + { + this.data = new VPKCRequestData(asn1.getData()); + } + else if (service == ServiceType.CCPD.getValue().intValue()) + { + this.data = new CCPDRequestData(asn1.getData()); + } + else + { + throw new DVCSConstructionException("Unknown service type: " + service); + } + } + + /** + * Return the ASN.1 DVCSRequest structure making up the body of this request. + * + * @return an org.spongycastle.asn1.dvcs.DVCSRequest object. + */ + public ASN1Encodable getContent() + { + return asn1; + } + + /** + * Get RequestInformation envelope. + * + * @return the request info object. + */ + public DVCSRequestInfo getRequestInfo() + { + return reqInfo; + } + + /** + * Get data of DVCRequest. + * Depending on type of the request it could be different subclasses of DVCRequestData. + * + * @return the request Data object. + */ + public DVCSRequestData getData() + { + return data; + } + + /** + * Get the transaction identifier of request. + * + * @return the GeneralName representing the Transaction Identifier. + */ + public GeneralName getTransactionIdentifier() + { + return asn1.getTransactionIdentifier(); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestBuilder.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestBuilder.java new file mode 100644 index 00000000..f1982b91 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestBuilder.java @@ -0,0 +1,131 @@ +package org.spongycastle.dvcs; + +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cms.ContentInfo; +import org.spongycastle.asn1.dvcs.DVCSObjectIdentifiers; +import org.spongycastle.asn1.dvcs.DVCSRequestInformationBuilder; +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.asn1.x509.ExtensionsGenerator; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.cms.CMSSignedDataGenerator; + +/** + * Common base class for client DVCRequest builders. + * This class aims at DVCSRequestInformation and TransactionIdentifier construction, + * and its subclasses - for Data field construction (as it is specific for the requested service). + */ +public abstract class DVCSRequestBuilder +{ + private final ExtensionsGenerator extGenerator = new ExtensionsGenerator(); + private final CMSSignedDataGenerator signedDataGen = new CMSSignedDataGenerator(); + + protected final DVCSRequestInformationBuilder requestInformationBuilder; + + protected DVCSRequestBuilder(DVCSRequestInformationBuilder requestInformationBuilder) + { + this.requestInformationBuilder = requestInformationBuilder; + } + + /** + * Set a nonce for this request, + * + * @param nonce + */ + public void setNonce(BigInteger nonce) + { + requestInformationBuilder.setNonce(nonce); + } + + /** + * Set requester name. + * + * @param requester + */ + public void setRequester(GeneralName requester) + { + requestInformationBuilder.setRequester(requester); + } + + /** + * Set DVCS name to generated requests. + * + * @param dvcs + */ + public void setDVCS(GeneralName dvcs) + { + requestInformationBuilder.setDVCS(dvcs); + } + + /** + * Set DVCS name to generated requests. + * + * @param dvcs + */ + public void setDVCS(GeneralNames dvcs) + { + requestInformationBuilder.setDVCS(dvcs); + } + + /** + * Set data location to generated requests. + * + * @param dataLocation + */ + public void setDataLocations(GeneralName dataLocation) + { + requestInformationBuilder.setDataLocations(dataLocation); + } + + /** + * Set data location to generated requests. + * + * @param dataLocations + */ + public void setDataLocations(GeneralNames dataLocations) + { + requestInformationBuilder.setDataLocations(dataLocations); + } + + /** + * Add a given extension field. + * + * @param oid the OID defining the extension type. + * @param isCritical true if the extension is critical, false otherwise. + * @param value the ASN.1 structure that forms the extension's value. + * @return this builder object. + * @throws DVCSException if there is an issue encoding the extension for adding. + */ + public void addExtension( + ASN1ObjectIdentifier oid, + boolean isCritical, + ASN1Encodable value) + throws DVCSException + { + try + { + extGenerator.addExtension(oid, isCritical, value); + } + catch (IOException e) + { + throw new DVCSException("cannot encode extension: " + e.getMessage(), e); + } + } + + protected DVCSRequest createDVCRequest(Data data) + throws DVCSException + { + if (!extGenerator.isEmpty()) + { + requestInformationBuilder.setExtensions(extGenerator.generate()); + } + + org.spongycastle.asn1.dvcs.DVCSRequest request = new org.spongycastle.asn1.dvcs.DVCSRequest(requestInformationBuilder.build(), data); + + return new DVCSRequest(new ContentInfo(DVCSObjectIdentifiers.id_ct_DVCSRequestData, request)); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestData.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestData.java new file mode 100644 index 00000000..35cdd325 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestData.java @@ -0,0 +1,38 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.dvcs.Data; + +/** + * Data piece of DVCRequest object (DVCS Data structure). + * Its contents depend on the service type. + * Its subclasses define the service-specific interface. + * <p/> + * The concrete objects of DVCRequestData are created by buildDVCRequestData static method. + */ +public abstract class DVCSRequestData +{ + /** + * The underlying data object is accessible by subclasses. + */ + protected Data data; + + /** + * The constructor is accessible by subclasses. + * + * @param data + */ + protected DVCSRequestData(Data data) + { + this.data = data; + } + + /** + * Convert to ASN.1 structure (Data). + * + * @return + */ + public Data toASN1Structure() + { + return data; + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestInfo.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestInfo.java new file mode 100644 index 00000000..1f51e3e6 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSRequestInfo.java @@ -0,0 +1,237 @@ +package org.spongycastle.dvcs; + +import java.math.BigInteger; +import java.util.Date; + +import org.spongycastle.asn1.dvcs.DVCSRequestInformation; +import org.spongycastle.asn1.dvcs.DVCSTime; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.PolicyInformation; +import org.spongycastle.tsp.TimeStampToken; +import org.spongycastle.util.Arrays; + +/** + * Information piece of DVCS requests. + * It is common for all types of DVCS requests. + */ +public class DVCSRequestInfo +{ + private DVCSRequestInformation data; + + /** + * Constructs DVCRequestInfo from byte array (DER encoded DVCSRequestInformation). + * + * @param in + */ + public DVCSRequestInfo(byte[] in) + { + this(DVCSRequestInformation.getInstance(in)); + } + + /** + * Constructs DVCRequestInfo from DVCSRequestInformation ASN.1 structure. + * + * @param data + */ + public DVCSRequestInfo(DVCSRequestInformation data) + { + this.data = data; + } + + /** + * Converts to corresponding ASN.1 structure (DVCSRequestInformation). + * + * @return + */ + public DVCSRequestInformation toASN1Structure() + { + return data; + } + + // + // DVCRequestInfo selector interface + // + + /** + * Get DVCS version of request. + * + * @return + */ + public int getVersion() + { + return data.getVersion(); + } + + /** + * Get requested service type. + * + * @return one of CPD, VSD, VPKC, CCPD (see constants). + */ + public int getServiceType() + { + return data.getService().getValue().intValue(); + } + + /** + * Get nonce if it is set. + * Note: this field can be set (if not present) or extended (if present) by DVCS. + * + * @return nonce value, or null if it is not set. + */ + public BigInteger getNonce() + { + return data.getNonce(); + } + + /** + * Get request generation time if it is set. + * + * @return time of request, or null if it is not set. + * @throws DVCSParsingException if a request time is present but cannot be extracted. + */ + public Date getRequestTime() + throws DVCSParsingException + { + DVCSTime time = data.getRequestTime(); + + if (time == null) + { + return null; + } + + try + { + if (time.getGenTime() != null) + { + return time.getGenTime().getDate(); + } + else + { + TimeStampToken token = new TimeStampToken(time.getTimeStampToken()); + + return token.getTimeStampInfo().getGenTime(); + } + } + catch (Exception e) + { + throw new DVCSParsingException("unable to extract time: " + e.getMessage(), e); + } + } + + /** + * Get names of requesting entity, if set. + * + * @return + */ + public GeneralNames getRequester() + { + return data.getRequester(); + } + + /** + * Get policy, under which the validation is requested. + * + * @return policy identifier or null, if any policy is acceptable. + */ + public PolicyInformation getRequestPolicy() + { + if (data.getRequestPolicy() != null) + { + return data.getRequestPolicy(); + } + return null; + } + + /** + * Get names of DVCS servers. + * Note: this field can be set by DVCS. + * + * @return + */ + public GeneralNames getDVCSNames() + { + return data.getDVCS(); + } + + /** + * Get data locations, where the copy of request Data can be obtained. + * Note: the exact meaning of field is up to applications. + * Note: this field can be set by DVCS. + * + * @return + */ + public GeneralNames getDataLocations() + { + return data.getDataLocations(); + } + + /** + * Compares two DVCRequestInfo structures: one from DVCRequest, and one from DVCResponse. + * This function implements RFC 3029, 9.1 checks of reqInfo. + * + * @param requestInfo - DVCRequestInfo of DVCRequest + * @param responseInfo - DVCRequestInfo of DVCResponse + * @return true if server's requestInfo matches client's requestInfo + */ + public static boolean validate(DVCSRequestInfo requestInfo, DVCSRequestInfo responseInfo) + { + // RFC 3029, 9.1 + // The DVCS MAY modify the fields: + // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure. + + DVCSRequestInformation clientInfo = requestInfo.data; + DVCSRequestInformation serverInfo = responseInfo.data; + + if (clientInfo.getVersion() != serverInfo.getVersion()) + { + return false; + } + if (!clientEqualsServer(clientInfo.getService(), serverInfo.getService())) + { + return false; + } + if (!clientEqualsServer(clientInfo.getRequestTime(), serverInfo.getRequestTime())) + { + return false; + } + if (!clientEqualsServer(clientInfo.getRequestPolicy(), serverInfo.getRequestPolicy())) + { + return false; + } + if (!clientEqualsServer(clientInfo.getExtensions(), serverInfo.getExtensions())) + { + return false; + } + + // RFC 3029, 9.1. The only modification allowed to a 'nonce' + // is the inclusion of a new field if it was not present, + // or to concatenate other data to the end (right) of an existing value. + + if (clientInfo.getNonce() != null) + { + if (serverInfo.getNonce() == null) + { + return false; + } + byte[] clientNonce = clientInfo.getNonce().toByteArray(); + byte[] serverNonce = serverInfo.getNonce().toByteArray(); + if (serverNonce.length < clientNonce.length) + { + return false; + } + if (!Arrays.areEqual(clientNonce, Arrays.copyOfRange(serverNonce, 0, clientNonce.length))) + { + return false; + } + } + + return true; + } + + // null-protected compare of any two objects + private static boolean clientEqualsServer(Object client, Object server) + { + return (client == null && server == null) || (client != null && client.equals(server)); + } +} + diff --git a/pkix/src/main/java/org/spongycastle/dvcs/DVCSResponse.java b/pkix/src/main/java/org/spongycastle/dvcs/DVCSResponse.java new file mode 100644 index 00000000..f7185519 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/DVCSResponse.java @@ -0,0 +1,74 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.cms.ContentInfo; +import org.spongycastle.asn1.cms.SignedData; +import org.spongycastle.asn1.dvcs.DVCSObjectIdentifiers; +import org.spongycastle.cms.CMSSignedData; + +/** + * DVCResponse is general response to DVCS (RFC 3029). + * It represents responses for all types of services. + */ +public class DVCSResponse + extends DVCSMessage +{ + private org.spongycastle.asn1.dvcs.DVCSResponse asn1; + + /** + * Constructs DVCRequest from CMS SignedData object. + * + * @param signedData the CMS SignedData object containing the request + * @throws org.spongycastle.dvcs.DVCSConstructionException + */ + public DVCSResponse(CMSSignedData signedData) + throws DVCSConstructionException + { + this(SignedData.getInstance(signedData.toASN1Structure().getContent()).getEncapContentInfo()); + } + + /** + * Construct a DVCS Request from a ContentInfo + * + * @param contentInfo the contentInfo representing the DVCSRequest + * @throws org.spongycastle.dvcs.DVCSConstructionException + */ + public DVCSResponse(ContentInfo contentInfo) + throws DVCSConstructionException + { + super(contentInfo); + + if (!DVCSObjectIdentifiers.id_ct_DVCSResponseData.equals(contentInfo.getContentType())) + { + throw new DVCSConstructionException("ContentInfo not a DVCS Request"); + } + + try + { + if (contentInfo.getContent().toASN1Primitive() instanceof ASN1Sequence) + { + this.asn1 = org.spongycastle.asn1.dvcs.DVCSResponse.getInstance(contentInfo.getContent()); + } + else + { + this.asn1 = org.spongycastle.asn1.dvcs.DVCSResponse.getInstance(ASN1OctetString.getInstance(contentInfo.getContent()).getOctets()); + } + } + catch (Exception e) + { + throw new DVCSConstructionException("Unable to parse content: " + e.getMessage(), e); + } + } + + /** + * Return the ASN.1 DVCSResponse structure making up the body of this response. + * + * @return an org.spongycastle.asn1.dvcs.DVCSResponse object. + */ + public ASN1Encodable getContent() + { + return asn1; + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/MessageImprint.java b/pkix/src/main/java/org/spongycastle/dvcs/MessageImprint.java new file mode 100644 index 00000000..c9793880 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/MessageImprint.java @@ -0,0 +1,38 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.x509.DigestInfo; + +public class MessageImprint +{ + private final DigestInfo messageImprint; + + public MessageImprint(DigestInfo messageImprint) + { + this.messageImprint = messageImprint; + } + + public DigestInfo toASN1Structure() + { + return messageImprint; + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof MessageImprint) + { + return messageImprint.equals(((MessageImprint)o).messageImprint); + } + + return false; + } + + public int hashCode() + { + return messageImprint.hashCode(); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/MessageImprintBuilder.java b/pkix/src/main/java/org/spongycastle/dvcs/MessageImprintBuilder.java new file mode 100644 index 00000000..f7dc987e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/MessageImprintBuilder.java @@ -0,0 +1,35 @@ +package org.spongycastle.dvcs; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.DigestInfo; +import org.spongycastle.operator.DigestCalculator; + +public class MessageImprintBuilder +{ + private final DigestCalculator digestCalculator; + + public MessageImprintBuilder(DigestCalculator digestCalculator) + { + this.digestCalculator = digestCalculator; + } + + public MessageImprint build(byte[] message) + throws DVCSException + { + try + { + OutputStream dOut = digestCalculator.getOutputStream(); + + dOut.write(message); + + dOut.close(); + + return new MessageImprint(new DigestInfo(digestCalculator.getAlgorithmIdentifier(), digestCalculator.getDigest())); + } + catch (Exception e) + { + throw new DVCSException("unable to build MessageImprint: " + e.getMessage(), e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/SignedDVCSMessageGenerator.java b/pkix/src/main/java/org/spongycastle/dvcs/SignedDVCSMessageGenerator.java new file mode 100644 index 00000000..2378bcf2 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/SignedDVCSMessageGenerator.java @@ -0,0 +1,45 @@ +package org.spongycastle.dvcs; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.cms.CMSException; +import org.spongycastle.cms.CMSProcessableByteArray; +import org.spongycastle.cms.CMSSignedData; +import org.spongycastle.cms.CMSSignedDataGenerator; + +public class SignedDVCSMessageGenerator +{ + private final CMSSignedDataGenerator signedDataGen; + + public SignedDVCSMessageGenerator(CMSSignedDataGenerator signedDataGen) + { + this.signedDataGen = signedDataGen; + } + + /** + * Creates a CMSSignedData object containing the passed in DVCSMessage + * + * @param message the request to be signed. + * @return an encapsulating SignedData object. + * @throws DVCSException in the event of failure to encode the request or sign it. + */ + public CMSSignedData build(DVCSMessage message) + throws DVCSException + { + try + { + byte[] encapsulatedData = message.getContent().toASN1Primitive().getEncoded(ASN1Encoding.DER); + + return signedDataGen.generate(new CMSProcessableByteArray(message.getContentType(), encapsulatedData), true); + } + catch (CMSException e) + { + throw new DVCSException("Could not sign DVCS request", e); + } + catch (IOException e) + { + throw new DVCSException("Could not encode DVCS request", e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/TargetChain.java b/pkix/src/main/java/org/spongycastle/dvcs/TargetChain.java new file mode 100644 index 00000000..d45714f3 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/TargetChain.java @@ -0,0 +1,18 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.dvcs.TargetEtcChain; + +public class TargetChain +{ + private final TargetEtcChain certs; + + public TargetChain(TargetEtcChain certs) + { + this.certs = certs; + } + + public TargetEtcChain toASN1Structure() + { + return certs; + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/VPKCRequestBuilder.java b/pkix/src/main/java/org/spongycastle/dvcs/VPKCRequestBuilder.java new file mode 100644 index 00000000..9a68b7d3 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/VPKCRequestBuilder.java @@ -0,0 +1,76 @@ +package org.spongycastle.dvcs; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.spongycastle.asn1.dvcs.CertEtcToken; +import org.spongycastle.asn1.dvcs.DVCSRequestInformationBuilder; +import org.spongycastle.asn1.dvcs.DVCSTime; +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.asn1.dvcs.ServiceType; +import org.spongycastle.asn1.dvcs.TargetEtcChain; +import org.spongycastle.asn1.x509.Extension; +import org.spongycastle.cert.X509CertificateHolder; + +/** + * Builder of DVC requests to VPKC service (Verify Public Key Certificates). + */ +public class VPKCRequestBuilder + extends DVCSRequestBuilder +{ + private List chains = new ArrayList(); + + public VPKCRequestBuilder() + { + super(new DVCSRequestInformationBuilder(ServiceType.VPKC)); + } + + /** + * Adds a TargetChain representing a X.509 certificate to the request. + * + * @param cert the certificate to be added + */ + public void addTargetChain(X509CertificateHolder cert) + { + chains.add(new TargetEtcChain(new CertEtcToken(CertEtcToken.TAG_CERTIFICATE, cert.toASN1Structure()))); + } + + /** + * Adds a TargetChain representing a single X.509 Extension to the request + * + * @param extension the extension to be added. + */ + public void addTargetChain(Extension extension) + { + chains.add(new TargetEtcChain(new CertEtcToken(extension))); + } + + /** + * Adds a X.509 certificate to the request. + * + * @param targetChain the CertChain object to be added. + */ + public void addTargetChain(TargetChain targetChain) + { + chains.add(targetChain.toASN1Structure()); + } + + public void setRequestTime(Date requestTime) + { + requestInformationBuilder.setRequestTime(new DVCSTime(requestTime)); + } + + /** + * Build DVCS request to VPKC service. + * + * @throws DVCSException + */ + public DVCSRequest build() + throws DVCSException + { + Data data = new Data((TargetEtcChain[])chains.toArray(new TargetEtcChain[chains.size()])); + + return createDVCRequest(data); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/VPKCRequestData.java b/pkix/src/main/java/org/spongycastle/dvcs/VPKCRequestData.java new file mode 100644 index 00000000..561f93bf --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/VPKCRequestData.java @@ -0,0 +1,51 @@ +package org.spongycastle.dvcs; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.asn1.dvcs.TargetEtcChain; + +/** + * Data piece of DVCS request to VPKC service (Verify Public Key Certificates). + * It contains VPKC-specific interface. + * <p/> + * This objects are constructed internally, + * to build DVCS request to VPKC service use VPKCRequestBuilder. + */ +public class VPKCRequestData + extends DVCSRequestData +{ + private List chains; + + VPKCRequestData(Data data) + throws DVCSConstructionException + { + super(data); + + TargetEtcChain[] certs = data.getCerts(); + + if (certs == null) + { + throw new DVCSConstructionException("DVCSRequest.data.certs should be specified for VPKC service"); + } + + chains = new ArrayList(certs.length); + + for (int i = 0; i != certs.length; i++) + { + chains.add(new TargetChain(certs[i])); + } + } + + /** + * Get contained certs choice data.. + * + * @return a list of CertChain objects. + */ + public List getCerts() + { + return Collections.unmodifiableList(chains); + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/VSDRequestBuilder.java b/pkix/src/main/java/org/spongycastle/dvcs/VSDRequestBuilder.java new file mode 100644 index 00000000..f2f7df54 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/VSDRequestBuilder.java @@ -0,0 +1,49 @@ +package org.spongycastle.dvcs; + +import java.io.IOException; +import java.util.Date; + +import org.spongycastle.asn1.dvcs.DVCSRequestInformationBuilder; +import org.spongycastle.asn1.dvcs.DVCSTime; +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.asn1.dvcs.ServiceType; +import org.spongycastle.cms.CMSSignedData; + +/** + * Builder of DVCS requests to VSD service (Verify Signed Document). + */ +public class VSDRequestBuilder + extends DVCSRequestBuilder +{ + public VSDRequestBuilder() + { + super(new DVCSRequestInformationBuilder(ServiceType.VSD)); + } + + public void setRequestTime(Date requestTime) + { + requestInformationBuilder.setRequestTime(new DVCSTime(requestTime)); + } + + /** + * Build VSD request from CMS SignedData object. + * + * @param document + * @return + * @throws DVCSException + */ + public DVCSRequest build(CMSSignedData document) + throws DVCSException + { + try + { + Data data = new Data(document.getEncoded()); + + return createDVCRequest(data); + } + catch (IOException e) + { + throw new DVCSException("Failed to encode CMS signed data", e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/dvcs/VSDRequestData.java b/pkix/src/main/java/org/spongycastle/dvcs/VSDRequestData.java new file mode 100644 index 00000000..21adaa3f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/dvcs/VSDRequestData.java @@ -0,0 +1,66 @@ +package org.spongycastle.dvcs; + +import org.spongycastle.asn1.dvcs.Data; +import org.spongycastle.cms.CMSException; +import org.spongycastle.cms.CMSSignedData; + +/** + * Data piece of DVCS request to VSD service (Verify Signed Document). + * It contains VSD-specific selector interface. + * Note: the request should contain CMS SignedData object as message. + * <p/> + * This objects are constructed internally, + * to build DVCS request to VSD service use VSDRequestBuilder. + */ +public class VSDRequestData + extends DVCSRequestData +{ + private CMSSignedData doc; + + VSDRequestData(Data data) + throws DVCSConstructionException + { + super(data); + initDocument(); + } + + private void initDocument() + throws DVCSConstructionException + { + if (doc == null) + { + if (data.getMessage() == null) + { + throw new DVCSConstructionException("DVCSRequest.data.message should be specified for VSD service"); + } + try + { + doc = new CMSSignedData(data.getMessage().getOctets()); + } + catch (CMSException e) + { + throw new DVCSConstructionException("Can't read CMS SignedData from input", e); + } + } + } + + /** + * Get contained message (data to be certified). + * + * @return + */ + public byte[] getMessage() + { + return data.getMessage().getOctets(); + } + + /** + * Get the CMS SignedData object represented by the encoded message. + * + * @return + */ + public CMSSignedData getParsedMessage() + { + return doc; + } +} |