Wednesday 24 September 2014

Public-Key Cryptography Standards: PKCS#7 Digital Signature

Public-Key Cryptography Standards: PKCS#7 Digital Signature

Introduction

Public key cryptography is based on asymmetric cryptographic algorithms that use two related keys, a public key and a private key; the two keys have property that, given the public key, it is computationally infeasible to derive the private key. Users publish public key in a public directory and keep their private key to them.

 An encryption algorithm could be used to encrypt a data using the private key so that only the recipient who has the corresponding public key could decrypt the data. A signature algorithm together with a message digest algorithm could be used to transform a message of any length using the private key to a signature in such a way that, without the knowledge of the private key, it is computationally infeasible to find two messages with the same signature, to find a message for a pre-determined signature, or to find a signature for a given message. Anyone who has the corresponding public key could verify the validity of the signature. Typical public key digital signature algorithms are RSA, DSA, and ECDSA.


What is PKCS#7

Public-Key Cryptography Standards (PKCS) are RSA Data Security, Inc.'s series of de-facto standard formats for public-key cryptography. Among all the PKCS standards, PKCS#7 is probably the most widely used one. It describes a general syntax for data that may have cryptography applied to it, such as digital signatures and digital envelopes.
The S/MIME secure mail standard uses PKCS#7 for its digitally signed and encrypted messages. Certificate requests and certificate store (.spc) files also normally use the PKCS#7 format. Every PKCS#7 blob usually encapsulates some content (such as an encrypted message or signed hash value) and one or more certificates used to encrypt or sign this content.


PKCS #7 Cryptographic Message Syntax (CMS)

PKCS #7 has been superseded by cryptographic message syntax (CMS), which is the basis for the S/MIME specification. CMS defines the syntax that is used to digitally sign, digest, authenticate, or encrypt arbitrary message content.

In particular, CMS describes encapsulation syntax for data protection. The syntax allows multiple encapsulations; one encapsulation envelope can be nested inside another. Likewise, one party can digitally sign some previously encapsulated data. In the CMS syntax, arbitrary attributes, such as signing time, can be signed along with the message content, and other attributes, such as countersignatures, can be associated with a signature.

PKCS#7 data content called the content type could be of two classes: Base and Enhanced.

v  Base Content Type:
§  Contains data without cryptographic enhancements.

v  Enhanced content type:
§  Contains data (possibly encrypted), and other cryptographic enhancements (such as hashes or signatures).
§  Employs encapsulation, giving rise to the terms outer content (the one containing the enhancements) and inner content (the one being enhanced).

Enhanced class might contain the data content type (Base class) that has a signature included with it. In this case, the data content type is the inner content and the combination of the data content type and the signature forms the outer content.
[Source: RFC 2315 - PKCS #7: Cryptographic Message Syntax and
http://msdn.microsoft.com/en-us/library/windows/desktop/aa387331(v=vs.85).aspx]

The content types defined in the PKCS #7 standards are as follows.
[Source: RFC 2315 - PKCS #7: Cryptographic Message Syntax and
http://msdn.microsoft.com/en-us/library/windows/desktop/aa387331(v=vs.85).aspx]

Content type
Description
Data
An octet (BYTE) string.
Signed Data
Content of any type and encrypted message hashes (digests) of the content for zero or more signers.
Enveloped Data
Encrypted content of any type and encrypted content-encryption keys for one or more recipients. The combination of encrypted content and encrypted content-encryption key for a recipient is a digital envelope for that recipient.
Signed-and-Enveloped Data
Encrypted content of any type, encrypted content-encryption keys for one or more recipients, and doubly encrypted message digests for one or more signers. The double encryption consists of an encryption with a signer's private key followed by an encryption with the content-encryption key.
Digested Data
Content of any type and a message hash (digest) of the content.
Encrypted Data
Encrypted content of any type. Unlike the enveloped-data content type, the encrypted-data content type has neither recipients nor encrypted content-encryption keys. Keys are assumed to be managed by other means.


Digital Signature Implementation

Digital signature implementation (PKCS#7) implementation has three major steps
  • The creation of private and public keys
  • Given a message and a private key: the production of the signature.
  • Given a signed message and a public key: the signature verification.



Generate Private Public Key Pair

$ keytool  -genkey  -keyalg RSA  -alias <alias>  -keypass <key_password>  -keystore  <keystore_name.jks>  - storepass  <store_password>  -  validity <validity>
 Note: Its recommended and expected that once a keystore has been created private public key pair will be generated per client so that every client has its own public key and server side implementation will use corresponding private key.


Extract Public Key

We can extract public key from the key store using the command
$ keytool -exportcert -alias <alias> -keypass<key_password> -keystore "keystore_name.jks" -rfc -file <certificate_name.pem>

 


Sign Data using Bouncy Castle

Code below is compatible with bcprov-jdk15on-1.50.jar and bcmail-jdk15on-1.50.jar library.
package com.test.pkcs;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public final class PKCS7BCSigner {
            private static final String PATH_TO_KEYSTORE = "<path_to_jks>";
            private static final String KEY_ALIAS_IN_KEYSTORE =  "<alias_client_privatekey>";
            private static final String KEYSTORE_PASSWORD = "<keystore_password>";
            private static final String SIGNATUREALGO = "SHA256withRSA";

            public PKCS7BCSigner() {
            }
            KeyStore loadKeyStore() throws Exception {
                        KeyStore keystore = KeyStore.getInstance("JKS");
                        InputStream is = new FileInputStream(PATH_TO_KEYSTORE);
                        keystore.load(is, KEYSTORE_PASSWORD.toCharArray());
                        return keystore;
            }
            CMSSignedDataGenerator setUpProvider(final KeyStore keystore)
                                    throws Exception {
                        Security.addProvider(new BouncyCastleProvider());
                        Certificate[] certchain = (Certificate[]) keystore
                                                .getCertificateChain(KEY_ALIAS_IN_KEYSTORE);
                        final List<Certificate> certlist = new ArrayList<Certificate>();
                        for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
                                    certlist.add(certchain[i]);
                        }
                        Store certstore = new JcaCertStore(certlist);
                        Certificate cert = keystore.getCertificate(KEY_ALIAS_IN_KEYSTORE);
                        ContentSigner signer = new JcaContentSignerBuilder(SIGNATUREALGO)
                                                .setProvider("BC").build(
                                                                        (PrivateKey) (keystore.getKey(KEY_ALIAS_IN_KEYSTORE,
                                                                        KEYSTORE_PASSWORD.toCharArray())));
                        CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
                        generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
                                                new JcaDigestCalculatorProviderBuilder().setProvider("BC")
                                                                        .build()).build(signer, (X509Certificate) cert));
                        generator.addCertificates(certstore);
                        return generator;
            }
            byte[] signPkcs7(final byte[] content,
                                    final CMSSignedDataGenerator generator) throws Exception {
                        CMSTypedData cmsdata = new CMSProcessableByteArray(content);
                        CMSSignedData signeddata = generator.generate(cmsdata, true);
                        return signeddata.getEncoded();
            }

            public static void main(String[] args) throws Exception {
                        PKCS7BCSigner signer = new PKCS7BCSigner();
                        KeyStore keyStore = signer.loadKeyStore();
                        CMSSignedDataGenerator signatureGenerator = signer
                                                .setUpProvider(keyStore);
                        String content = "<some_bytes_to_be _signed>";
                        byte[] signedBytes = signer.signPkcs7(content.getBytes("UTF-8"),
                                                signatureGenerator);
                        System.out.println("Signed Encoded Bytes: "
                                                + new String(Base64.encode(signedBytes)));
            }
}


Signature Verification using Bouncy Castle

package com.test.pkcs;

import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public class PKCS7SignatureVerifier {
            public static void main(String[] args) throws Exception {
                        String envelopedData = “<signed_data>”;
                        Security.addProvider(new BouncyCastleProvider());
                        CMSSignedData cms = new CMSSignedData(Base64.decode(envelopedData
                                                .getBytes()));
                        Store store = cms.getCertificates();
                        SignerInformationStore signers = cms.getSignerInfos();
                        Collection c = signers.getSigners();
                        Iterator it = c.iterator();
                        while (it.hasNext()) {
                                    SignerInformation signer = (SignerInformation) it.next();
                                    Collection certCollection = store.getMatches(signer.getSID());
                                    Iterator certIt = certCollection.iterator();
                                    X509CertificateHolder certHolder = (X509CertificateHolder) certIt
                                                            .next();
                                    X509Certificate cert = new JcaX509CertificateConverter()
                                                            .setProvider("BC").getCertificate(certHolder);
                                    if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder()
                                                            .setProvider("BC").build(cert))) {
                                                System.out.println("verified");
                                                CMSProcessableByteArray cpb = (CMSProcessableByteArray) cms
                                                                        .getSignedContent();
                                                byte[] rawcontent = (byte[]) cpb.getContent();
                                                System.out.println(new String(rawcontent));
                                    }
                        }
            }

}

Mutual Authentication

Mutual Authentication

Introduction

Mutual authentication, also called two-way authentication, is a process for both entities in a communications link to authenticate each other. In network environments, client authenticates the server and vice-versa to ensure that they are doing business with legitimate entities. With mutual authentication, SSL connection can occur only when the client trusts the server's digital certificate and the server trusts the client's certificate. The exchange of certificates is carried out by means of the Transport Layer Security (TLS) protocol.
A well-designed mutual authentication solution also protects against online fraud such as man in the middle attacks, shoulder surfing, Trojan horses, key loggers and pharming.
To ensure optimum security, mutual authentication can be used in conjunction with other countermeasures such as firewalls, antivirus software and anti-spyware programs.

Configure SSL Mutual (Two-way) Authentication

Let’s explore how to create a 2 way authentication (mutual authentication) in SSL reverse proxy balancer gateway. This configuration is useful in any enterprise environment where it’s requested to separate clients, the frontend and the backend, and when the traffic between clients and the gateway, and between the gateway and the backbends must be encrypted which ensures the clients and the backbends to be authentic, and avoids Man in the Middle attacks.
Since the reverse proxy is in the middle between the clients and the backbends, it’s requested for the clients to send a known client certificate to the gateway (apache), so that the gateway can recognize them. This is done with X509 certificates.
For the same reason, each backend contacted by the gateway is requested to respond with a valid and known server certificate. This is also done with X509 certificates.
Generally, the clients and the backbends will also check their peer’s (apache) certificate to be known and valid, so that if someone is going to impersonate the gateway, it will be found and will not be considered authentic.
To do so, we’ll use:
  • apache httpd
  •  mod_ssl
  •  mod_proxy_balancer + mod_proxy + mod_proxy_http
  • openssl

Everything is done with a simple and single virtual host in apache to be included in httpd.conf or ssl.conf file.

Mutual Authentication flow



Mutual Authentication Using Self Signed Certificate

Apache 2 and OpenSSL provide a useful, easy-to-configure and cost-effective mutual SSL/TLS authentication development and test environment with the help of the following components
·         Apache 2.0 web server with built-in mod_ssl support on Linux/UNIX
·         OpenSSL on Linux/UNIX


Creating the CA Key and CA Certificate

Do the steps below to generate the root CA certificate.
$ openssl genrsa –out ca.key 1024
$ openssl req -new -key ca.key -out ca.csr
$ openssl x509 -req -days 365 -in ca.csr -signkey ca.csr -out ca.pem
To verify the certificate we can execute the command
$ openssl x509 –in ca.pem –text


Creating Client Key and Client Certificate

Do the steps below to generate the root CA certificate.
$ openssl genrsa –out client.key 1024
$ openssl req –new –key client.key –out client.csr
$ openssl ca -in client.csr –cert ca.pem –keyfile ca.key –out client.pem
To verify the certificate we can execute the command
$ openssl x509 –in client.pem –text


Importing the client certificate in PKCS#12 format

Firefox and Internet Explorer 6.0 support the PKCS#12 certificate format. Use the following command to convert the user certificate to this format.
NOTE: During the conversion process, you’ll be asked for an export password. Enter anything you can remember, but don’t let it be empty because the file will contain your private key.
$ openssl pkcs12 –export –clcerts –in client.pem –inkey client.key –out client.p12


Intermediate Certificate

An intermediate certificate is the certificate, or certificates, that go between your site (server) certificate and a root certificate. The intermediate certificate, or certificates, completes the chain to a root certificate trusted by the browser.
We can create several intermediate certificates during authentication process the chain has to be completed and client must be signed by root CA.


Configuring Apache to Support Mutual Authentication

<VirtualHost *:443>
    # Apche server certificate
    SSLCertificateFile “/opt/apache/conf/ssl/server.pem”
   # Apache server private key
   SSLCertificateKeyFile “/opt/apache/conf/ssl/key.pem”
   # Apache server CA certificate (certificate of who released your server certificate)
   SSLCertificateChainFile “/opt/apache/conf/ssl/ca.pem”
   # Client’s CA certificates (list of certificates of who released your client’s certificates)
   SSLCACertificateFile “/opt/apache/conf/ssl/ca.pem”
   # It’s mandatory for apache to authenticate the client’s certificate
   SSLVerifyClient require
   <Location /url/to/be/restricted>
         SSLVerifyDepth <exact_numeric_value>
         # If CN verification is required
 
         SSLRequire %{SSL_CLIENT_S_DN_CN} in {  "ABC"}
   </Location>
</VirtualHost>
Note:  SSLVerifyDepth value is very important if root CA signed the client certificate it will be 2. If any intermediate CA is involved we need to add those.


Mutual Authentication for Trusted Certificate

If root CA is trusted we can also use SSLCACertificatePath which ideally will be /etc/ssl/certs where all the ca certs symbolic hash is created during ca-certs package installation.


Testing

$ curl -v --cert client.pem:<client_pem_password> --key client.key -X POST '<SECURE_URL>'

Console output will display some message which includes
* About to connect() to <host_server_details> 443 (#0)
*   Trying <ip_address_of_host>...
* Connected to < host_server_details > <ip_address_of_host> port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
<handshaking_details>
* Server certificate:
*      <server_certificate_info>
*      SSL certificate verify ok.
> POST <URL> HTTP/1.1
> User-Agent: curl/7.32.0
> Host: <host_name>
> Accept: */*
>
<Sslv3 TLS handshaking_details>
< HTTP/1.1 200 OK
< Set-Cookie: JSESSIONID=940071268FF51D4D4E2B70887FAF2DBA.oopsnode1; Path=/; Secure; HttpOnly
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
< Access-Control-Max-Age: 3600
< Access-Control-Allow-Headers: Authorization,Origin,X-Requested-With,Content-Type,Accept,merchantAccessKey,requestSignature
< Content-Length: 0
< Vary: Accept-Encoding
< Content-Type: text/plain; charset=UTF-8
<
* Connection #0 to host <host_name> left intact


Important Notes

1.       If using intermediate certificates for SSLCACertificate then we need to concatenate those certificates in a single large file according to reverse creation order like
$ cat ca-certificates.crt > ca-extended.crt
$ cat ca.pem root.pem   >> ca-exetnded.crt
(root.pem is the root CA and ca.pem is an intermediate created from root ca)

2.       Based on Apache and openssl version it may be required to put  SLCACertficate,SSLVerifyClient  and SSLVerifyDepth outside VirtualHost.