//* Licensed Materials - Property of *
//* IBM *
//* Miracle A/S *
//* Alexandra Instituttet A/S *
//* *
//* eu.abc4trust.pabce.1.34 *
//* *
//* (C) Copyright IBM Corp. 2014. All Rights Reserved. *
//* (C) Copyright Miracle A/S, Denmark. 2014. All Rights Reserved. *
//* (C) Copyright Alexandra Instituttet A/S, Denmark. 2014. All *
//* Rights Reserved. *
//* US Government Users Restricted Rights - Use, duplication or *
//* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. *
//* *
//* This file is licensed under the Apache License, Version 2.0 (the *
//* "License"); you may not use this file except in compliance with *
//* the License. You may obtain a copy of the License at: *
//* http://www.apache.org/licenses/LICENSE-2.0 *
//* Unless required by applicable law or agreed to in writing, *
//* software distributed under the License is distributed on an *
//* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
//* KIND, either express or implied. See the License for the *
//* specific language governing permissions and limitations *
//* under the License. *
//*/**/****************************************************************
package eu.abc4trust.smartcard;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
import com.ibm.zurich.idmix.abc4trust.facades.IssuerParametersFacade;
import com.ibm.zurich.idmix.abc4trust.facades.PseudonymCryptoFacade;
import com.ibm.zurich.idmix.abc4trust.facades.SmartcardParametersFacade;
import com.ibm.zurich.idmx.buildingBlock.signature.cl.ClPublicKeyWrapper;
import com.ibm.zurich.idmx.buildingBlock.systemParameters.EcryptSystemParametersWrapper;
import com.ibm.zurich.idmx.exception.ConfigurationException;
import eu.abc4trust.abce.internal.user.credentialManager.CredentialManagerImpl;
import eu.abc4trust.cryptoEngine.CryptoEngineException;
import eu.abc4trust.cryptoEngine.user.PseudonymSerializer;
import eu.abc4trust.cryptoEngine.user.PseudonymSerializerObjectGzip;
import eu.abc4trust.guice.ProductionModuleFactory.CryptoEngine;
import eu.abc4trust.xml.CryptoParams;
import eu.abc4trust.xml.IssuerParameters;
import eu.abc4trust.xml.Metadata;
import eu.abc4trust.xml.ObjectFactory;
import eu.abc4trust.xml.Pseudonym;
import eu.abc4trust.xml.PseudonymMetadata;
import eu.abc4trust.xml.PseudonymWithMetadata;
import eu.abc4trust.xml.SmartcardSystemParameters;
import eu.abc4trust.xml.SystemParameters;
public class SmartcardInitializeTool {
private static final byte[] accesscode = new byte[]{(byte) 0xDD, (byte) 0xE8, (byte) 0x90, (byte) 0x96, 0x3E, (byte) 0xF8, 0x09, 0x0E};
//================================== Group Parameters ===================================
//U-Prove Recommended Parameters (L2048N256)
//OID = 1.3.6.1.4.1.311.75.1.1.1
public static final BigInteger p = new BigInteger("ef0990061db67a9eaeba265f1b8fa12b553390a8175bcb3d0c2e5ee5dfb826e229ad37431148ce31f8b0e531777f19c1e381c623e600bff7c55a23a8e649ccbcf833f2dba99e6ad66e52378e92f7492b24ff8c1e6fb189fa8434f5402fe415249ae02bf92b3ed8eaaaa2202ec3417b2079da4f35e985bb42a421cfaba8160b66949983384e56365a4486c046229fc8c818f930b80a60d6c2c2e20c5df880534d4240d0d81e9a370eef676a1c3b0ed1d8ff30340a96b21b89f69c54ceb8f3df17e31bc20c5b601e994445a1d347a45d95f41ae07176c7380c60db2aceddeeda5c5980964362e3a8dd3f973d6d4b241bcf910c7f7a02ed3b60383a0102d8060c27", 16);
public static final BigInteger q = new BigInteger("c8f750941d91791904c7186d62368ec19e56b330b669d08708f882e4edb82885", 16);
//public static final BigInteger Gd = new BigInteger("487813c6d3efc50b646745573142de47649cc77789aa545d2fca97e9e5e94639810fda34e77cff614b3a86715c7ae093a1070987b183c3c7efa892e3dca1f98fcfaa39e1d649aaae00f89473db7c8cf92037ad771fc464cb6b76f18325a1b02ea41d29276a1cf9b9bd7b25bb5f9a219ab022c7ab8d25378bcc7b9ffcdb70971c03d320fbff71797338ff24007bd785cfbdaaf4bb219b079b96382dff211e23f554092c3aa8af79e8a60d21355e7d026b3c8207fe4feeaca8a9a8dc5fc8333817c67bf805bbe0c032b10839a9026ba9c9bb120bd4ceebacd3152b66b256e41e4a06224ba3f2d3ab99a26c364fe822c0d2c5e972545c2572561c795fbb68a34018", 16);
//public static final BigInteger h = new BigInteger("bca29a2d4b226f594591ecedbd1859ccb0ba3d20186b30e0ffbf05ba25788a6720005194c1f005b2ced980ca160254bb48a0e2d756ddcc919afe9017a47905154177fb2c37fb6cc0f4423e8f4a8b8376e0043dddf06255050523d4ee1f68748d0d415732686f01d88d98c75bd1e25fa48cd5bf4cc69b6d67bf0dd5c9cf18ee91ae17ebf128151286de3ab17ac4025a91168d42532144b7357e423f1b8d9dbcee68df89b44150e496ff6d416e4376e2daf9e422807d276572cec335d0587a5d798022415e3737326251d304fd7129183357ef9c8d194447705360b5bb270a2ce6194e5894c1fafad3ca78af080f500227564d43cb63462b1084e9ccd55d002e19", 16);
//Gd is the device generator used to encode the device secret as: Gd^x
//p,q,g defines the group, but it seems like g is not used. However, I keep it here just in case:
//public static final BigInteger g = new BigInteger("bca29a2d4b226f594591ecedbd1859ccb0ba3d20186b30e0ffbf05ba25788a6720005194c1f005b2ced980ca160254bb48a0e2d756ddcc919afe9017a47905154177fb2c37fb6cc0f4423e8f4a8b8376e0043dddf06255050523d4ee1f68748d0d415732686f01d88d98c75bd1e25fa48cd5bf4cc69b6d67bf0dd5c9cf18ee91ae17ebf128151286de3ab17ac4025a91168d42532144b7357e423f1b8d9dbcee68df89b44150e496ff6d416e4376e2daf9e422807d276572cec335d0587a5d798022415e3737326251d304fd7129183357ef9c8d194447705360b5bb270a2ce6194e5894c1fafad3ca78af080f500227564d43cb63462b1084e9ccd55d002e19", 16);
private static final PseudonymSerializer pseudonymSerializer = new PseudonymSerializerObjectGzip(new CardStorage());
// Signing Keys
public static boolean TEST_KEYS = false;
public static final int SIGNING_KEY_LENGTH = 1024;
public static final int MAC_KEY_LENGTH = 128;
//
final RSAKeyPair rootKey;
final SystemParameters systemParameters;
final URI pseudonymScope;
public SmartcardInitializeTool(RSAKeyPair rootKey, SystemParameters systemParameters, URI scope) {
this.rootKey = rootKey;
this.systemParameters = systemParameters;
this.pseudonymScope = scope;
}
CryptoEngine cryptoEngine;
List<IssuerParameters> issuerParametersList;
public void setIssuerParameters(CryptoEngine cryptoEngine, List<IssuerParameters> issuerParameters) {
this.cryptoEngine = cryptoEngine;
this.issuerParametersList = issuerParameters;
}
CryptoEngine cryptoEngine_counterCredential;
IssuerParameters issuerParameters_counterCredential;
RSAVerificationKey coursePublicKey;
public void setIssuerParametersForCounterCredential(CryptoEngine cryptoEngine, IssuerParameters issuerParameters, RSAVerificationKey coursePublicKey) {
this.cryptoEngine_counterCredential = cryptoEngine;
this.issuerParameters_counterCredential = issuerParameters;
this.coursePublicKey = coursePublicKey;
System.err.println("issuerParams: "+issuerParameters);
}
public class InitializeResult {
final BigInteger pseudonymValue;
final int puk;
public InitializeResult(BigInteger pv, int puk) {
this.pseudonymValue = pv;
this.puk = puk;
}
public BigInteger getPseudonymValue() {
return pseudonymValue;
}
public int getPuk() {
return puk;
}
}
public InitializeResult initializeSmartcard(Smartcard smartcard, int pin, short deviceID, URI deviceURI,
int minAttendance) throws IOException, ClassNotFoundException, Exception,
UnsupportedEncodingException {
System.out.println("about to initialize the smartcard");
eu.abc4trust.smartcard.SystemParameters scSysParams = createSmartcardSystemParameters(systemParameters);
//Ensure that the output folder exists
int puk = -1;
if(smartcard instanceof HardwareSmartcard){
if(((HardwareSmartcard)smartcard).getMode() == 0){
((HardwareSmartcard) smartcard).setRootMode(accesscode);
}
}
if(!smartcard.wasInit()){
puk = smartcard.init(pin, scSysParams, rootKey, deviceID);
if(puk == -1){
throw new Exception("Initialization failed. Aborting!");
}
}
//Now we attach an issuer based on the engine type
for(IssuerParameters issuerParam : issuerParametersList) {
URI issuerUri = signIssuerParameters(cryptoEngine, issuerParam, systemParameters, smartcard, rootKey, q, p);
System.out.println("Signed Issuer : " + issuerUri);
//InitializeSmartcard.checkIssuerParameters(cryptoEngine, issuerParameters, pin, s);
}
if(cryptoEngine_counterCredential != null) {
URI issuerUri = signIssuerParametersWithAttendance(cryptoEngine_counterCredential, issuerParameters_counterCredential, systemParameters, smartcard, rootKey, 1, coursePublicKey, minAttendance, q, p);
System.out.println("Signed Issuer with Attendance : " + issuerUri);
}
if(smartcard instanceof HardwareSmartcard){
((HardwareSmartcard)smartcard).setWorkingMode();
}
System.out.println("ISSUER PARAMS ADDED!");
//now that we have a pin on the card, we store the deviceURI
SmartcardBlob blob = new SmartcardBlob();
blob.blob = deviceURI.toASCIIString().getBytes("US-ASCII");
smartcard.storeBlob(pin, Smartcard.device_name, blob);
//Generate pseudonym
BigInteger pseudonymValue = smartcard.computeScopeExclusivePseudonym(pin, pseudonymScope);
PseudonymWithMetadata pwm = generatePseudonymWithMetadata(cryptoEngine, deviceURI, pseudonymValue, deviceID, pseudonymScope);
// store on card
URI pseudonymUri = pwm.getPseudonym().getPseudonymUID();
if(pseudonymUri.toString().contains(":") && !pseudonymUri.toString().contains("_")){
pseudonymUri = URI.create(pseudonymUri.toString().replaceAll(":", "_")); //change all ':' to '_'
}
pseudonymUri = URI.create(CredentialManagerImpl.PSEUDONYM_PREFIX+pseudonymUri.toString());
SmartcardStatusCode code = smartcard.storePseudonym(pin, pseudonymUri, pwm, pseudonymSerializer);
if(code == SmartcardStatusCode.OK){
System.out.println("Pseudonym with uid "+pseudonymUri +" stored on the smartcard.");
}else{
System.err.println("Storing pseudonym on card failed with status: "+code);
throw new Exception("Storing pseudonym on card failed with status: "+code);
}
return new InitializeResult(pseudonymValue, puk);
}
private static void checkIssuerParameters(CryptoEngine engine, IssuerParameters ip, int pin, Smartcard sc){
if(engine.equals(CryptoEngine.IDEMIX)){
IssuerParametersFacade ipw = new IssuerParametersFacade(ip);
ClPublicKeyWrapper pkw = new ClPublicKeyWrapper(ipw.getPublicKey());
BigInteger issuer_n;
try {
issuer_n = pkw.getModulus().getValue();
} catch (ConfigurationException e) {
throw new RuntimeException(e);
}
TrustedIssuerParameters tip = sc.getIssuerParameters(pin, ip.getParametersUID());
BigInteger card_n = tip.groupParams.getModulus();
if(!issuer_n.equals(card_n)){
throw new RuntimeException("n's for issuer parameter "+ip.getParametersUID()+" differed from the ones just put on the card!" +
"\n ip_n: "+issuer_n+"\n card_n: "+ card_n);
}
System.out.println("Issuer parameter "+ip.getParametersUID()+" passed the equality test");
}else{
System.out.println("Not checking U-Prove parameters as the moduli is the same for all issuers.");
}
}
public static PseudonymWithMetadata generatePseudonymWithMetadata(CryptoEngine cryptoEngine , URI secretUid, BigInteger pseudonymValue, int notPinButNumber, URI scope) {
ObjectFactory of = new ObjectFactory();
Pseudonym pseudonym = of.createPseudonym();
pseudonym.setSecretReference(secretUid);
pseudonym.setExclusive(true);
pseudonym.setPseudonymUID(scope);
pseudonym.setPseudonymValue(pseudonymValue.toByteArray());
pseudonym.setScope(scope.toString());
Metadata md = of.createMetadata();
PseudonymMetadata pmd = of.createPseudonymMetadata();
pmd.setHumanReadableData("Pregenerated pseudonym");
pmd.setMetadata(md);
PseudonymWithMetadata pwm = of.createPseudonymWithMetadata();
pwm.setPseudonym(pseudonym);
pwm.setPseudonymMetadata(pmd);
CryptoParams cryptoEvidence = of.createCryptoParams();
URI groupParameterId = URI.create("http://www.zurich.ibm.com/security/idmx/v2/gp.xml");
PseudonymCryptoFacade pcf = new PseudonymCryptoFacade();
pcf.setScopeExclusivePseudonym(scope, secretUid, pseudonym.getPseudonymValue());
pwm.setCryptoParams(pcf.getCryptoParams());
return pwm;
}
public static void appendToPseudonymFile(File pseudonymFile, short ID, CryptoEngine cryptoEngine, BigInteger pseudonymValue) throws IOException {
String lineFeed = System.getProperty("line.separator","\n");
String pseudonymEncoded = URLEncoder.encode(Base64.encodeBytes(pseudonymValue.toByteArray()), "UTF-8");
String data = ID+";"+pseudonymEncoded+";"+cryptoEngine+lineFeed;
appendToFile(pseudonymFile, data.getBytes("UTF-8"));
}
public static short getID(String line) {
String[] split = line.split(";");
return Short.parseShort(split[0]);
}
public static String getPseudonymB64(String line) throws UnsupportedEncodingException {
String[] split = line.split(";");
return URLDecoder.decode(split[1], "UTF-8");
}
public static BigInteger getPseudonymBigInteger(String line) throws UnsupportedEncodingException, IOException {
String[] split = line.split(";");
String b64 = URLDecoder.decode(split[1], "UTF-8");
byte[] bytes = Base64.decode(b64);
return new BigInteger(bytes);
}
public static CryptoEngine getCryptoEngine(String line) throws UnsupportedEncodingException {
String[] split = line.split(";");
return CryptoEngine.valueOf(split[2]);
}
public static void appendToFile(File f, byte[] data) throws IOException {
FileOutputStream fos = new FileOutputStream(f, true);
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
fos));
out.write(data);
out.flush();
out.close();
}
private URI signIssuerParametersWithAttendance(CryptoEngine engine, IssuerParameters issuerParameters, SystemParameters sp, Smartcard ssc, RSAKeyPair sk_root,
int keyIDForCounter, RSAVerificationKey coursePk, int minimumAttendance, BigInteger q, BigInteger p) {
System.out.println("signIssuerParametersWithAttendance");
System.out.println("Getting smartcardParametersFacade for sp: "+sp+", and issuer params: "+issuerParameters);
SmartcardParametersFacade spf = new SmartcardParametersFacade(sp, issuerParameters);
SmartcardParameters credBases;
try {
credBases = spf.getSmartcardParameters();
System.out.println("credBases, generator1: "+credBases.getBaseForDeviceSecret());
System.out.println("credBases, generator2: "+credBases.getBaseForCredentialSecretOrNull());
} catch (CryptoEngineException e) {
throw new RuntimeException(e);
}
ssc.getNewNonceForSignature();
System.out.println("params URI : " + issuerParameters.getParametersUID());
URI parametersUri = issuerParameters.getParametersUID();
SmartcardStatusCode result =
ssc.addIssuerParametersWithAttendanceCheck(sk_root, parametersUri, keyIDForCounter, credBases, coursePk, minimumAttendance);
System.out.println("RESULT OF ADDING!" + result);
if(! (result == SmartcardStatusCode.OK)) {
throw new IllegalStateException("Could not add issuer params..." + result);
}
return parametersUri;
}
private URI signIssuerParameters(CryptoEngine engine, IssuerParameters issuerParameters, SystemParameters sp, Smartcard ssc, RSAKeyPair sk_root,
BigInteger q, BigInteger p) {
System.out.println("signIssuerParameters");
SmartcardParametersFacade spf = new SmartcardParametersFacade(sp, issuerParameters);
SmartcardParameters credBases;
try {
credBases = spf.getSmartcardParameters();
} catch (CryptoEngineException e) {
throw new RuntimeException(e);
}
ssc.getNewNonceForSignature();
System.out.println("params URI : " + issuerParameters.getParametersUID());
URI parametersUri = issuerParameters.getParametersUID();
SmartcardStatusCode result = ssc.addIssuerParameters(sk_root, parametersUri, credBases);
System.out.println("RESULT OF ADDING! " + result);
if(! (result == SmartcardStatusCode.OK)) {
throw new IllegalStateException("Could not add issuer params... " + result);
}
return parametersUri;
}
// copy from ABCE-COMPONENTS
private /*SmartcardSystemParameters*/ eu.abc4trust.smartcard.SystemParameters createSmartcardSystemParameters(SystemParameters sysParams) {
SmartcardSystemParameters scSysParams = new SmartcardSystemParameters();
EcryptSystemParametersWrapper spw = new EcryptSystemParametersWrapper(systemParameters);
BigInteger p, g, subgroupOrder;
try {
p = spw.getDHModulus().getValue();
g = spw.getDHGenerator1().getValue();
subgroupOrder = spw.getDHSubgroupOrder().getValue();
} catch (ConfigurationException e1) {
throw new RuntimeException(e1);
}
int zkChallengeSizeBytes = 256 / 8;
int zkStatisticalHidingSizeBytes = 80 / 8;
int deviceSecretSizeBytes = 256 / 8;
int signatureNonceLengthBytes = 128 / 8;
int zkNonceSizeBytes = 256 / 8;
int zkNonceOpeningSizeBytes = 256 / 8;
scSysParams.setPrimeModulus(p);
scSysParams.setGenerator(g);
scSysParams.setSubgroupOrder(subgroupOrder);
scSysParams.setZkChallengeSizeBytes(zkChallengeSizeBytes);
scSysParams.setZkStatisticalHidingSizeBytes(zkStatisticalHidingSizeBytes);
scSysParams.setDeviceSecretSizeBytes(deviceSecretSizeBytes);
scSysParams.setSignatureNonceLengthBytes(signatureNonceLengthBytes);
scSysParams.setZkNonceSizeBytes(zkNonceSizeBytes);
scSysParams.setZkNonceOpeningSizeBytes(zkNonceOpeningSizeBytes);
// return scSysParams;
return new eu.abc4trust.smartcard.SystemParameters(scSysParams);
}
// Signing Key Tools Methods
@SuppressWarnings("unused")
public static void generateSignatureKeys(String signatureKeysFolder, String signatureKeysPrefix)
throws IOException {
System.out.println("generateSignatureKeys : " + signatureKeysPrefix);
//if (signatureKeysPrefix.startsWith("pki")) {
// sk_root = eu.abc4trust.smartcard.RSASignatureSystemTest.getSigningKeyForTest();
//} else {
// sk_root = eu.abc4trust.smartcard.RSASignatureSystemTest.getAnotherSigningKeyForTest();
//}
RSAKeyPair sk_root = RSASignatureSystem.generateSigningKey(SIGNING_KEY_LENGTH/8);
RSAVerificationKey pk_root = RSASignatureSystem.getVerificationKey(sk_root);
// TODO Verify if files exists...
storeObjectInFile(sk_root, signatureKeysFolder + "/" + signatureKeysPrefix, "_sk");
storeObjectInFile(pk_root, signatureKeysFolder + "/" + signatureKeysPrefix, "_pk");
}
// Tools Methods
public static RSAKeyPair loadPrivateKey(String resourse) throws IOException,
ClassNotFoundException {
RSAKeyPair privateKey = loadObjectFromResource(resourse);
return privateKey;
}
public static RSAVerificationKey loadPublicKey(String resourse) throws IOException,
ClassNotFoundException {
RSAVerificationKey publicKey = loadObjectFromResource(resourse);
return publicKey;
}
private static void storeObjectInFile(Object object, String prefix, String name)
throws IOException {
File file = new File(prefix + name);
storeObjectInFile(object, file);
}
private static void storeObjectInFile(Object object, File file)
throws IOException {
System.out.println("storeObject " + object + " - in file " + file.getAbsolutePath());
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(object);
fos.close();
}
@SuppressWarnings("unchecked")
private static <T> T loadObjectFromResource(String name) throws IOException,
ClassNotFoundException {
System.out.println("Read Params Object from resouce : " + name);
InputStream is = getInputStream(name);
ObjectInputStream ois = new ObjectInputStream(is);
Object object = ois.readObject();
ois.close();
is.close();
return (T) object;
}
protected static InputStream getInputStream(String resource) throws IOException {
InputStream is = SmartcardInitializeTool.class.getResourceAsStream(resource);
if (is == null) {
File f = new File(resource);
if (!f.exists()) {
throw new IllegalStateException("Resource not found : " + resource);
}
is = new FileInputStream(f);
}
return is;
}
public static String toHex(byte[] mac) {
StringBuilder macStr = new StringBuilder(); // "hex:");
for (byte element : mac) {
String hex = String.format("%02x", element);
// System.out.println("- " + hex + " == " + mac[j]);
macStr.append(hex);
}
return macStr.toString();
}
}