Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* This software is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.cesecore.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.cesecore.certificates.ca.internal.SernoGeneratorRandom;
import org.cesecore.certificates.util.DnComponents;
/**
* Helper class to handle operations for RFC4683 certificate extension Subject Identification method (SIM) for
* including a privacy-sensitive identifier in the subjectAltName extension of a certificate. The SIM is
* an optional feature that may be used by relying parties to determine whether the subject of a particular
* certificate is also the person corresponding to a particular sensitive identifier
* (see {@link https://tools.ietf.org/html/rfc4683}).
*
* @version $Id: RFC4683Tools.java 25261 2017-02-14 13:14:29Z anatom $
*/
public final class RFC4683Tools {
/** List separator to separate the SIM tokens in the internal storage format (also has to be entered by the user). */
public static final String LIST_SEPARATOR = "::";
/** Label for SIM rendered in the certificate. */
public static final String SUBJECTIDENTIFICATIONMETHOD = "subjectIdentificationMethod";
/** OID for SIM written into the certificate. */
public static final String SUBJECTIDENTIFICATIONMETHOD_OBJECTID = "1.3.6.1.5.5.7.8.6";
private static final Logger LOG = Logger.getLogger(RFC4683Tools.class);
/**
* Gets the allowed hash algorithm object identifiers (see: {@link https://tools.ietf.org/html/rfc4683#section-4.3}).
* @return a list of ASN1ObjectIdentifier {@link TSPAlgorithms#ALLOWED}.
*/
@SuppressWarnings("unchecked")
public static final List getAllowedHashAlgorithms() {
return new ArrayList(TSPAlgorithms.ALLOWED);
}
/**
* Gets the allowed hash algorithm OID strings.
* @return a list of OID strings {@link TSPAlgorithms#ALLOWED}.
*/
public static final List getAllowedHashAlgorithmOidStrings() {
final List identifiers = getAllowedHashAlgorithms();
final List result = new ArrayList(identifiers.size());
for (ASN1ObjectIdentifier identifier : identifiers) {
result.add(identifier.getId());
}
return result;
}
/** This method reads the internal storage format for SAN.
* If the SAN contains SIM parameters (list of 4 tokens, separated by '::'), the parameters are replaced by
* the generated SIM strings (list of 3 tokens, separated by '::') {@link RFC4683Tools#generateInternalSimString(String, String, String, String)}
*
* @param san the SAN string in internal storage format with SIM as user parameters.
* @return SAN string in internal storage format with generated SIM strings.
*/
public static final String generateSimForInternalSanFormat(String san)
throws IllegalArgumentException, NoSuchProviderException, NoSuchAlgorithmException {
if (StringUtils.isNotBlank(san) && san.contains(DnComponents.SUBJECTIDENTIFICATIONMETHOD)) {
final List sims = CertTools.getPartsFromDN(san, DnComponents.SUBJECTIDENTIFICATIONMETHOD);
for (String sim : sims) {
if (LOG.isDebugEnabled()) {
LOG.info("Store user SIM strings: " + sims);
}
if (StringUtils.isNotBlank(sim)) {
final String[] tokens = sim.split(LIST_SEPARATOR);
// was entered as hash, password, SSIType and SSI, so generate the SIM
if (tokens.length == 4) {
final String newSim = generateInternalSimString(tokens[0], tokens[1], tokens[2], tokens[3]);
san = san.replace(sim, newSim);
} else if (tokens.length == 3) {
// NOOP
} else {
throw new IllegalArgumentException("Wrong SIM input string with " + tokens.length + " tokens.");
}
}
}
}
return san;
}
/**
* Creates a '::' separated string of hashAlogrithmOidString, Authority Random (R) and Privacy-Enhanced Protected Subject Information (PEPSI).
* Note: RFC4683 Subject Identification Method (SIM = R || PEPSI), and PEPSI = H(H( P || R || SIItype || SII)). The resulting String is used
* for internal storage.
*
* Where R is the Authority Random hash and PEPSI the Privacy-Enhanced Protected Subject Information:
* PEPSI = H(H( P || R || SIItype || SII))
* Where P is the user chosen password, SSI the Sensitive Identification Information and SIIType its type.
*
* @param hashAlogrithmOidString i.e '1.3.14.3.2.26' for SHA-1
* @param userChosenPassword (P) FIPS 112 and FIPS 180-1 compliant password up to 28 characters (see https://tools.ietf.org/html/rfc4683#section-4.2)
* @param ssiType OID string of an SSI type (see https://tools.ietf.org/html/rfc4683#section-4.1).
* @param ssi Sensitive Identification Information (SII) (see https://tools.ietf.org/html/rfc4683#section-4.1).
* @return a '::' separated string of hashAlogrithmOidString, R and PEPSI.
* @throws IllegalArgumentException
*/
public static final String generateInternalSimString(final String hashAlogrithmOidString, final String userChosenPassword, final String ssiType,
final String ssi) throws IllegalArgumentException, NoSuchProviderException, NoSuchAlgorithmException {
if (StringUtils.isBlank(hashAlogrithmOidString)) {
throw new IllegalArgumentException("Hash algorithm OID string must not be null or empty: '" + hashAlogrithmOidString + "'.");
}
if (!getAllowedHashAlgorithmOidStrings().contains(hashAlogrithmOidString)) {
throw new IllegalArgumentException("Hash algorithm with OID '" + hashAlogrithmOidString + "' is not supparted for RFC4683 (SIM).");
}
// TODO Insert check for FIPS 180-1 compliant passwords (better current standards ...)
if (StringUtils.isBlank(userChosenPassword) || userChosenPassword.length() < 8) {
throw new IllegalArgumentException("The user chosen password must not be null or empty: '" + hashAlogrithmOidString + "'.");
}
if (StringUtils.isBlank(ssiType)) {
throw new IllegalArgumentException("The sensitve identification information type must not be null or empty: '" + ssiType + "'.");
}
if (StringUtils.isBlank(ssi)) {
throw new IllegalArgumentException("The sensitve identification information must not be null or empty: '" + ssi + "'.");
}
final StringBuilder result = new StringBuilder();
result.append(hashAlogrithmOidString);
// 1. Create authority random.
final String authorityRandomSource = Long.toHexString(SernoGeneratorRandom.instance().getSerno().longValue());
if (LOG.isDebugEnabled()) {
LOG.debug("Authority random source created: " + authorityRandomSource);
}
// 1b. Get HEX by hash of authority random.
final MessageDigest digester = MessageDigest.getInstance(new ASN1ObjectIdentifier(hashAlogrithmOidString).getId(),
BouncyCastleProvider.PROVIDER_NAME);
digester.update(authorityRandomSource.getBytes());
final String authorityRandom = toHexString(digester.digest());
result.append(LIST_SEPARATOR).append(authorityRandom);
if (LOG.isDebugEnabled()) {
LOG.debug("Authority random hash created: " + authorityRandom);
}
// 2. Create SIM HEX string, and hash 2 times.
digester.update(
new StringBuilder().append(userChosenPassword).append(authorityRandomSource).append(ssiType).append(ssi).toString().getBytes());
digester.update(digester.digest());
final String pepsi = toHexString(digester.digest());
result.append(LIST_SEPARATOR).append(pepsi);
if (LOG.isDebugEnabled()) {
LOG.debug("SIM string PEPSI created: " + pepsi);
}
return result.toString();
}
/**
* Creates a SIM GeneralName by the internal SIM storage format ('hashAlgorithmOIDString::R::PEPSI')
* SIM ::= SEQUENCE { hashAlg AlgorithmIdentifier, authorityRandom OCTET
* STRING, -- RA-chosen random number -- used in computation of -- pEPSI
* pEPSI OCTET STRING -- hash of HashContent -- with algorithm hashAlg }
*
* @param hashAlogrithmOidString the OID string for the hash algorithm used to hash R and PEPSI.
* @param authorityRandom the registration authority chosen random value, hashed with hash of hashAlogrithmOidString (see https://tools.ietf.org/html/rfc4683#section-4.3).
* @param pepsi Privacy-Enhanced Protected Subject Information (PEPSI), with SIM = R || PEPSI.
* @return the RFC4683 SIM GeneralName (see {@link https://tools.ietf.org/html/rfc4683#section-4.3}).
*/
public static final ASN1Primitive createSimGeneralName(final String hashAlgorithmIdentifier, final String authorityRandom, final String pepsi) {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating SIM with hash algorithem identifier " + hashAlgorithmIdentifier + ", authority random " + authorityRandom
+ " and PEPSI " + pepsi);
}
final ASN1EncodableVector otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier(SUBJECTIDENTIFICATIONMETHOD_OBJECTID));
final ASN1EncodableVector simVector = new ASN1EncodableVector();
simVector.add(new AlgorithmIdentifier(new ASN1ObjectIdentifier(hashAlgorithmIdentifier))); // new DERTaggedObject(true, 0,
simVector.add(new DEROctetString((authorityRandom).getBytes()));
simVector.add(new DEROctetString((pepsi).getBytes()));
otherName.add(new DERTaggedObject(true, 0, new DERSequence(simVector)));
final ASN1Primitive generalName = new DERTaggedObject(false, 0, new DERSequence(otherName));
if (LOG.isDebugEnabled()) {
LOG.debug("GeneralName (type 0 - OtherName) for SIM created " + generalName.toString());
}
return generalName;
}
/**
* Helper method for getting the SIM name from SAN ASN.1 sequence.
*
* @param sequence the OtherName sequence
* @return the SIM string by the otherName.
*/
public static String getSimStringSequence(final ASN1Sequence sequence) {
if (LOG.isDebugEnabled()) {
LOG.debug("Parsing RFC4683 (SIM) from SAN ASN.1 sequence: " + sequence);
}
String result = null;
if (sequence != null) {
// First in sequence is the object identifier, that we must check
final ASN1ObjectIdentifier id = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0));
if (SUBJECTIDENTIFICATIONMETHOD_OBJECTID.equals(id.getId())) {
final ASN1Sequence simVector = (ASN1Sequence) ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
// 1. After certificate issuance the method is called with an algorithm identifier in its ASN.1 sequence.
// 2. But after reading a stored certificate (PEM or DER) the ASN.1 sequence contains a DERSeqence instead.
String algorithmIdentifier = null;
if (simVector.getObjectAt(0) instanceof AlgorithmIdentifier) {
algorithmIdentifier = ((AlgorithmIdentifier) simVector.getObjectAt(0)).getAlgorithm().getId();
} else {
final ASN1Encodable encodable = ((ASN1Sequence) simVector.getObjectAt(0)).getObjectAt(0);
algorithmIdentifier = encodable.toASN1Primitive().toString();
}
final ASN1OctetString hash = (ASN1OctetString) simVector.getObjectAt(1);
final ASN1OctetString pepsi = (ASN1OctetString) simVector.getObjectAt(2);
final String hashString = new String(hash.getOctets());
final String pepsiString = new String(pepsi.getOctets());
final StringBuilder builder = new StringBuilder();
result = builder.append(algorithmIdentifier).append(LIST_SEPARATOR).append(hashString).append(LIST_SEPARATOR).append(pepsiString)
.toString();
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("SIM parsed from other name: " + result);
}
return result;
}
/**
* The method generates the HEX string by digestResult.
* @param digestResult the resulting byte[] of the digester.
* @return the HEX string.
*/
public static final String toHexString(final byte[] digestResult) {
final StringBuffer buf = new StringBuffer(digestResult.length * 2);
for (int i = 0; i < digestResult.length; i++) {
int intVal = digestResult[i] & 0xff;
if (intVal < 0x10) {
buf.append("0");
}
buf.append(Integer.toHexString(intVal).toUpperCase());
}
return buf.toString();
}
/** Avoid instantiation. */
private RFC4683Tools() {
}
}