Class GenericCertificate

  • All Implemented Interfaces:
    Serializable

    public final class GenericCertificate
    extends Object
    implements Serializable
    This non-visual JavaBean implements authenticated runtime objects whose integrity cannot be compromised without being detected. The idea and the design of this class is inspired by both SignedObject and Certificate.

    More specifically, a GenericCertificate contains an XML string encoded representation of an arbitrary object in the "encoded" property and a Base64 immutable string representation of the object's corresponding digital signature in the "signature" property. The selection of this representation form and the design of this class as a plain JavaBean allows its instances to be serialized using either this package's PersistenceService, JDK's XMLEncoder, or the vanilla ObjectOutputStream.

    For an object to be successfully digitally signed, it must support serialisation via JDK's XMLEncoder, for which this package provides the class PersistenceService. This easy-to-use class allows you to provide custom PersistenceDelegate instances for the serialisation of any classes which do not implement the JavaBean design pattern and are not supported by XMLEncoder as a default.

    Whenever an instance of this GenericCertificate class is created, you can arbitrarily set and get its "encoded" and "signature" properties, allowing you to provide even custom deserialisation methods other than this class already provides via the aforementioned classes. However, once this instance is used to either sign or verify another object it gets locked, allowing subsequent read access to its properties only.

    The underlying signing algorithm is designated by the Signature object passed to the sign and the verify methods.

    A typical usage for signing is the following: <pre> GenericCertificate cert = new GenericCertificate(); Signature signingEngine = Signature.getInstance(algorithm, provider); try { cert.sign(myObject, signingKey, signingEngine); } catch (PropertyVetoException signingVetoed) { // ... } catch (PersistenceServiceException serialisationFailed) { // ... } catch (InvalidKeyException invalidKey) { // ... } catch (SignatureException signingEngineBroken) { // ... } </pre> A typical usage for verification is the following (having received GenericCertificate cert): <pre> Signature verificationEngine = Signature.getInstance(algorithm, provider); try { cert.verify(publicKey, verificationEngine)); } catch (PropertyVetoException verificationVetoed) { // ... } catch (InvalidKeyException invalidKey) { // ... } catch (SignatureException verificationEngineBroken) { // ... } catch (GenericCertificateException integrityCompromised) { // ... } Object myObject = cert.getContent(); </pre> Several points are worth noting:

    • There is no need to initialize the signing or verification engine, as it will be re-initialized inside the sign(java.lang.Object, java.security.PrivateKey, java.security.Signature) and verify(java.security.PublicKey, java.security.Signature) methods. Secondly, for verification to succeed, the specified public key must be the public key corresponding to the private key used to sign the GenericCertificate.
    • In contrast to SignedObject, this class adds more security as it is impossible to retrieve the signed object without verifying the signature before. A SignedObject however could be deserialised from a compromised file and the application developer may erraticaly forget to call the SignedObject.verify(java.security.PublicKey, java.security.Signature) method before retrieving the signed object by calling SignedObject.getObject().
    • More importantly, for flexibility reasons, the sign() and verify() methods allow for customized signature engines, which can implement signature algorithms that are not installed formally as part of a crypto provider. However, it is crucial that the programmer writing the verifier code be aware what Signature engine is being used, as its own implementation of the Signature.verify(byte[]) method is invoked to verify a signature. In other words, a malicious Signature engine may choose to always return true on verification in an attempt to bypass a security check.
    • The signature algorithm can be, among others, the NIST standard DSA, using DSA and SHA-1. The algorithm is specified using the same convention as that for signatures. The DSA algorithm using the SHA-1 message digest algorithm can be specified, for example, as "SHA/DSA" or "SHA-1/DSA" (they are equivalent). In the case of RSA, there are multiple choices for the message digest algorithm, so the signing algorithm could be specified as, for example, "MD2/RSA", "MD5/RSA" or "SHA-1/RSA". The algorithm name must be specified, as there is no default.
    • The name of the Cryptography Package Provider is designated by the Signature parameter to the sign() and verify() methods. If the provider is not specified, the default provider is used. Each installation can be configured to use a particular provider as default.
    • The property change listeners are not persistet when using ObjectOutputStream or XMLEncoder.
    • Object.equals(Object) and Object.hashCode() are not overridden by this class because different JVMs will produce different literal encodings of the same object and we cannot rely on a proper equals(...) implementation in the class of a signed object.
    Potential applications of GenericCertificate include:
    • It can be used internally to any Java runtime as an unforgeable authorization token -- one that can be passed around without the fear that the token can be maliciously modified without being detected.
    • It can be used to sign and serialize data/object for storage outside the Java runtime (e.g., storing critical access control data on disk).
    • Nested GenericCertificates can be used to construct a logical sequence of signatures, resembling a chain of authorization and delegation.

    This class is thread-safe.

    Author:
    Christian Schlichtherle
    See Also:
    Signature, SignedObject, Certificate, Serialized Form
    • Field Detail

      • XML_CHARSET

        public static final String XML_CHARSET
        "UTF-8" - the character encoding used by XMLEncoder and XMLDecoder.
        See Also:
        Constant Field Values
      • DEFAULT_BUFSIZE

        public static final int DEFAULT_BUFSIZE
        10KB - the default buffer size for buffered I/O.
        See Also:
        Constant Field Values
    • Constructor Detail

      • GenericCertificate

        public GenericCertificate()
        Creates a new generic certificate.
      • GenericCertificate

        public GenericCertificate​(GenericCertificate cert)
        Copy constructor for the given generic certificate. Note that the new certificate is unlocked and does not have any event listeners.