GordianCompositeKeyPairGenerator.java
/*
* GordianKnot: Security Suite
* Copyright 2012-2026. Tony Washer
*
* 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 io.github.tonywasher.joceanus.gordianknot.impl.core.keypair;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairFactory;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairGenerator;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairSpec;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianIOException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianCompositeKeyPair.GordianStateAwareCompositeKeyPair;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import java.io.IOException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
/**
* CompositeKeyPair generator.
*/
public class GordianCompositeKeyPairGenerator
implements GordianKeyPairGenerator {
/**
* The keyPairSpec.
*/
private final GordianKeyPairSpec theSpec;
/**
* The keyPairFactory.
*/
private final GordianKeyPairFactory theFactory;
/**
* The list of generators.
*/
private final List<GordianKeyPairGenerator> theGenerators;
/**
* is the composite keyPair stateAware?.
*/
private final boolean isStateAware;
/**
* Constructor.
*
* @param pFactory the asymFactory.
* @param pSpec the keyPairSetSpec.
* @throws GordianException on error
*/
GordianCompositeKeyPairGenerator(final GordianKeyPairFactory pFactory,
final GordianKeyPairSpec pSpec) throws GordianException {
/* Store the spec. */
theSpec = pSpec;
theFactory = pFactory;
theGenerators = new ArrayList<>();
boolean stateAware = false;
/* Loop through the asymKeySpecs */
final Iterator<GordianKeyPairSpec> myIterator = pSpec.keySpecIterator();
while (myIterator.hasNext()) {
final GordianKeyPairSpec mySpec = myIterator.next();
/* create generator and add it to list */
theGenerators.add(pFactory.getKeyPairGenerator(mySpec));
stateAware = mySpec.isStateAware();
}
/* Record stateAwareness */
isStateAware = stateAware;
}
@Override
public GordianKeyPairSpec getKeySpec() {
return theSpec;
}
@Override
public GordianCompositeKeyPair generateKeyPair() {
/* Create the new empty keyPairSet */
final GordianCompositeKeyPair myKeyPair = isStateAware ? new GordianStateAwareCompositeKeyPair(theSpec)
: new GordianCompositeKeyPair(theSpec);
/* Loop through the generators */
for (GordianKeyPairGenerator myGenerator : theGenerators) {
/* create keyPair and add it to composite */
myKeyPair.addKeyPair(myGenerator.generateKeyPair());
}
/* Return the keyPair */
return myKeyPair;
}
@Override
public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
/* Protect against exceptions */
try {
/* Create the new empty keyPair */
final GordianCompositeKeyPair myPair = (GordianCompositeKeyPair) pKeyPair;
/* Loop through the generators */
final Iterator<GordianKeyPair> myIterator = myPair.iterator();
final ASN1EncodableVector ks = new ASN1EncodableVector();
for (GordianKeyPairGenerator myGenerator : theGenerators) {
/* create encodedKeySpec and add it to set */
ks.add(SubjectPublicKeyInfo.getInstance(myGenerator.getX509Encoding(myIterator.next()).getEncoded()));
}
/* Build the x509 encoding */
final AlgorithmIdentifier myId = new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite);
final SubjectPublicKeyInfo myInfo = new SubjectPublicKeyInfo(myId, new DERSequence(ks).getEncoded());
return new X509EncodedKeySpec(myInfo.getEncoded());
} catch (IOException e) {
throw new GordianIOException("Failed to derive keySpec", e);
}
}
@Override
public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
/* Protect against exceptions */
try {
/* Create the new empty keyPair */
final GordianCompositeKeyPair myPair = (GordianCompositeKeyPair) pKeyPair;
/* Loop through the generators */
final Iterator<GordianKeyPair> myIterator = myPair.iterator();
final ASN1EncodableVector ks = new ASN1EncodableVector();
for (GordianKeyPairGenerator myGenerator : theGenerators) {
/* create encodedKeySpec and add it to set */
ks.add(PrivateKeyInfo.getInstance(myGenerator.getPKCS8Encoding(myIterator.next()).getEncoded()));
}
final AlgorithmIdentifier myId = new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite);
final PrivateKeyInfo myInfo = new PrivateKeyInfo(myId, new DERSequence(ks));
return new PKCS8EncodedKeySpec(myInfo.getEncoded());
} catch (IOException e) {
throw new GordianIOException("Failed to derive keySpec", e);
}
}
@Override
public GordianCompositeKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKeySet,
final PKCS8EncodedKeySpec pPrivateKeySet) throws GordianException {
/* CheckKeySpecs */
checkKeySpec(pPublicKeySet);
checkKeySpec(pPrivateKeySet);
/* Protect against exceptions */
try {
/* Parse the Public ASN1 */
final ASN1Sequence myPubSequence = ASN1Sequence.getInstance(pPublicKeySet.getEncoded());
final SubjectPublicKeyInfo myPubInfo = SubjectPublicKeyInfo.getInstance(myPubSequence);
final ASN1Sequence myPubKeys = ASN1Sequence.getInstance(myPubInfo.getPublicKeyData().getBytes());
/* Parse the Private ASN1 */
final ASN1Sequence myPrivSequence = ASN1Sequence.getInstance(pPrivateKeySet.getEncoded());
final PrivateKeyInfo myPrivInfo = PrivateKeyInfo.getInstance(myPrivSequence);
final ASN1Sequence myPrivKeys = ASN1Sequence.getInstance(myPrivInfo.getPrivateKey().getOctets());
/* Create the keySet */
final GordianCompositeKeyPair myPair = isStateAware ? new GordianStateAwareCompositeKeyPair(theSpec)
: new GordianCompositeKeyPair(theSpec);
/* Build the list from the keys sequence */
final Enumeration<?> enPub = myPubKeys.getObjects();
final Enumeration<?> enPriv = myPrivKeys.getObjects();
for (GordianKeyPairGenerator myGenerator : theGenerators) {
final SubjectPublicKeyInfo myPubKInfo = SubjectPublicKeyInfo.getInstance(enPub.nextElement());
final X509EncodedKeySpec myX509 = new X509EncodedKeySpec(myPubKInfo.getEncoded());
final PrivateKeyInfo myPrivKInfo = PrivateKeyInfo.getInstance(enPriv.nextElement());
final PKCS8EncodedKeySpec myPKCS8 = new PKCS8EncodedKeySpec(myPrivKInfo.getEncoded());
myPair.addKeyPair(myGenerator.deriveKeyPair(myX509, myPKCS8));
}
/* return the composite pair */
return myPair;
/* Handle exceptions */
} catch (IOException e) {
throw new GordianIOException("Failed to encode keySpec", e);
}
}
@Override
public GordianCompositeKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pPublicKeySet) throws GordianException {
/* CheckKeySpecs */
checkKeySpec(pPublicKeySet);
/* Protect against exceptions */
try {
/* Parse the ASN1 */
final ASN1Sequence mySequence = ASN1Sequence.getInstance(pPublicKeySet.getEncoded());
final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(mySequence);
final ASN1Sequence myKeys = ASN1Sequence.getInstance(myInfo.getPublicKeyData().getBytes());
/* Create the keySet */
final GordianCompositeKeyPair myPair = new GordianCompositeKeyPair(theSpec);
/* Build the list from the keys sequence */
final Enumeration<?> en = myKeys.getObjects();
for (GordianKeyPairGenerator myGenerator : theGenerators) {
final SubjectPublicKeyInfo myPKInfo = SubjectPublicKeyInfo.getInstance(en.nextElement());
final X509EncodedKeySpec myX509 = new X509EncodedKeySpec(myPKInfo.getEncoded());
myPair.addKeyPair(myGenerator.derivePublicOnlyKeyPair(myX509));
}
/* return the composite pair */
return myPair;
/* Handle exceptions */
} catch (IOException e) {
throw new GordianIOException("Failed to encode keySpec", e);
}
}
/**
* Check keySpec.
*
* @param pKeySpec the keySpec.
* @throws GordianException on error
*/
private void checkKeySpec(final PKCS8EncodedKeySpec pKeySpec) throws GordianException {
final GordianKeyPairSpec myKeySpec = theFactory.determineKeyPairSpec(pKeySpec);
if (!theSpec.equals(myKeySpec)) {
throw new GordianDataException("KeySpec not supported by this KeyPairGenerator");
}
}
/**
* Check keySpec.
*
* @param pKeySpec the keySpec.
* @throws GordianException on error
*/
private void checkKeySpec(final X509EncodedKeySpec pKeySpec) throws GordianException {
final GordianKeyPairSpec myKeySpec = theFactory.determineKeyPairSpec(pKeySpec);
if (!theSpec.equals(myKeySpec)) {
throw new GordianDataException("KeySpec not supported by this KeyPairGenerator");
}
}
}