All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.getlime.security.powerauth.crypto.client.activation.PowerAuthClientActivation Maven / Gradle / Ivy

There is a newer version: 1.9.0
Show newest version
/*
 * PowerAuth Crypto Library
 * Copyright 2018 Wultra s.r.o.
 *
 * 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.getlime.security.powerauth.crypto.client.activation;

import com.google.common.io.BaseEncoding;
import io.getlime.security.powerauth.crypto.lib.generator.KeyGenerator;
import io.getlime.security.powerauth.crypto.lib.model.ActivationStatusBlobInfo;
import io.getlime.security.powerauth.crypto.lib.model.ActivationVersion;
import io.getlime.security.powerauth.crypto.lib.model.exception.CryptoProviderException;
import io.getlime.security.powerauth.crypto.lib.model.exception.GenericCryptoException;
import io.getlime.security.powerauth.crypto.lib.util.*;

import javax.crypto.SecretKey;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

/**
 * Class implementing a cryptography used on the client side in order
 * to complete the PowerAuth Client activation.
 *
 * @author Petr Dvorak
 *
 */
public class PowerAuthClientActivation {

    private final SignatureUtils signatureUtils = new SignatureUtils();
    private final KeyGenerator keyGenerator = new KeyGenerator();
    private final KeyConvertor keyConvertor = new KeyConvertor();

    /**
     * Verify the signature of activation code using Master Public Key.
     *
     * @param activationCode Activation code.
     * @param signature Activation data signature.
     * @param masterPublicKey Master Public Key.
     * @return Returns "true" if the signature matches activation data, "false" otherwise.
     * @throws InvalidKeyException If provided master public key is invalid.
     * @throws GenericCryptoException In case signature computation fails.
     * @throws CryptoProviderException In case cryptography provider is incorrectly initialized.
     */
    public boolean verifyActivationCodeSignature(String activationCode, byte[] signature, PublicKey masterPublicKey) throws InvalidKeyException, GenericCryptoException, CryptoProviderException {
        byte[] bytes = activationCode.getBytes(StandardCharsets.UTF_8);
        return signatureUtils.validateECDSASignature(bytes, signature, masterPublicKey);
    }

    /**
     * Generate a device related activation key pair.
     *
     * @return A new device key pair.
     * @throws CryptoProviderException In case cryptography provider is incorrectly initialized.
     */
    public KeyPair generateDeviceKeyPair() throws CryptoProviderException {
        return keyGenerator.generateKeyPair();
    }

    /**
     * Generate a new activation nonce.
     *
     * @return A new activation nonce.
     */
    public byte[] generateActivationNonce() {
        return keyGenerator.generateRandomBytes(16);
    }

    /**
     * Method computes the signature of the activation data in order to prove that a correct
     * client application is attempting to complete the activation.
     *
     * 

PowerAuth protocol versions: *

    *
  • 2.0
  • *
  • 2.1
  • *
* * This method is obsolete for PowerAuth protocol version 3.0 and will be deprecated in a future release. * * @param activationIdShort Short activation ID. * @param activationNonce Client activation nonce. * @param encryptedDevicePublicKey Encrypted device public key. * @param applicationKey Application identifier. * @param applicationSecret Application secret. * @return Signature bytes. * @throws GenericCryptoException In case hash computation fails. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. */ public byte[] computeApplicationSignature(String activationIdShort, byte[] activationNonce, byte[] encryptedDevicePublicKey, byte[] applicationKey, byte[] applicationSecret) throws GenericCryptoException, CryptoProviderException { String signatureBaseString = activationIdShort + "&" + BaseEncoding.base64().encode(activationNonce) + "&" + BaseEncoding.base64().encode(encryptedDevicePublicKey) + "&" + BaseEncoding.base64().encode(applicationKey); return new HMACHashUtilities().hash(applicationSecret, signatureBaseString.getBytes(StandardCharsets.UTF_8)); } /** * Encrypt a device public key using the activation OTP. * *

PowerAuth protocol versions: *

    *
  • 2.0
  • *
  • 2.1
  • *
* * @param devicePublicKey Device public key to be encrypted. * @param clientEphemeralPrivateKey Ephemeral private key. * @param masterPublicKey Master public key. * @param activationOTP Activation OTP value. * @param activationIdShort Short activation ID. * @param activationNonce Activation nonce, used as an initialization vector for AES encryption. * @return An encrypted device public key. * @throws InvalidKeyException In case provided public key is invalid. * @throws GenericCryptoException In case encryption fails. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. */ public byte[] encryptDevicePublicKey(PublicKey devicePublicKey, PrivateKey clientEphemeralPrivateKey, PublicKey masterPublicKey, String activationOTP, String activationIdShort, byte[] activationNonce) throws InvalidKeyException, GenericCryptoException, CryptoProviderException { byte[] activationIdShortBytes = activationIdShort.getBytes(StandardCharsets.UTF_8); SecretKey otpBasedSymmetricKey = keyGenerator.deriveSecretKeyFromPassword(activationOTP, activationIdShortBytes); byte[] devicePubKeyBytes = keyConvertor.convertPublicKeyToBytes(devicePublicKey); SecretKey ephemeralKey = keyGenerator.computeSharedKey(clientEphemeralPrivateKey, masterPublicKey); AESEncryptionUtils aes = new AESEncryptionUtils(); byte[] tmpData = aes.encrypt(devicePubKeyBytes, activationNonce, otpBasedSymmetricKey); return aes.encrypt(tmpData, activationNonce, ephemeralKey); } /** * Verify signature of the encrypted activation ID and server public key * using a Master Public Key. * * @param activationId Activation ID * @param C_serverPublicKey Encrypted server public key. * @param signature Encrypted server public key signature. * @param masterPublicKey Master Public Key. * @return Returns "true" if signature matches encrypted data, "false" otherwise. * @throws InvalidKeyException If provided master public key is invalid. * @throws GenericCryptoException In case signature computation fails. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. */ public boolean verifyServerDataSignature(String activationId, byte[] C_serverPublicKey, byte[] signature, PublicKey masterPublicKey) throws InvalidKeyException, GenericCryptoException, CryptoProviderException { byte[] activationIdBytes = activationId.getBytes(StandardCharsets.UTF_8); String activationIdBytesBase64 = BaseEncoding.base64().encode(activationIdBytes); String C_serverPublicKeyBase64 = BaseEncoding.base64().encode(C_serverPublicKey); byte[] result = (activationIdBytesBase64 + "&" + C_serverPublicKeyBase64).getBytes(StandardCharsets.UTF_8); return signatureUtils.validateECDSASignature(result, signature, masterPublicKey); } /** * Decrypt server public key using activation OTP and device private key. As a technical component for public key encryption, an ephemeral public key is * used (in order to deduce ephemeral symmetric key using ECDH). * *

PowerAuth protocol versions: *

    *
  • 2.0
  • *
  • 2.1
  • *
* * @param C_serverPublicKey Encrypted server public key. * @param devicePrivateKey Device private key. * @param ephemeralPublicKey Ephemeral public key. * @param activationOTP Activation OTP value. * @param activationIdShort Short activation OTP. * @param activationNonce Activation nonce, used as an initialization vector for AES encryption. * @return Decrypted server public key. * @throws InvalidKeyException In case some of the provided keys is invalid. * @throws InvalidKeySpecException In case key spec is invalid. * @throws GenericCryptoException In case decryption fails. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. */ public PublicKey decryptServerPublicKey(byte[] C_serverPublicKey, PrivateKey devicePrivateKey, PublicKey ephemeralPublicKey, String activationOTP, String activationIdShort, byte[] activationNonce) throws InvalidKeyException, InvalidKeySpecException, GenericCryptoException, CryptoProviderException { SecretKey ephemeralSymmetricKey = keyGenerator.computeSharedKey(devicePrivateKey, ephemeralPublicKey); byte[] activationIdShortBytes = activationIdShort.getBytes(StandardCharsets.UTF_8); SecretKey otpBasedSymmetricKey = keyGenerator.deriveSecretKeyFromPassword(activationOTP, activationIdShortBytes); AESEncryptionUtils aes = new AESEncryptionUtils(); byte[] decryptedTMP = aes.decrypt(C_serverPublicKey, activationNonce, ephemeralSymmetricKey); byte[] decryptedServerPublicKeyBytes = aes.decrypt(decryptedTMP, activationNonce, otpBasedSymmetricKey); return keyConvertor.convertBytesToPublicKey(decryptedServerPublicKeyBytes); } /** * Compute a fingerprint for the version 2 activation. The fingerprint can be used for visual validation of exchanged device public key. * *

PowerAuth protocol versions: *

    *
  • 2.0
  • *
  • 2.1
  • *
* * @param devicePublicKey Device public key. * @return Fingerprint of the public key. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. * @throws GenericCryptoException In case fingerprint could not be calculated. */ public String computeActivationFingerprint(PublicKey devicePublicKey) throws GenericCryptoException, CryptoProviderException { return computeActivationFingerprint(devicePublicKey, null, null, ActivationVersion.VERSION_2); } /** * Compute a fingerprint for the version 3 activation. The fingerprint can be used for visual validation of exchanged device public key. * *

PowerAuth protocol versions: *

    *
  • 3.0
  • *
* * @param devicePublicKey Device public key. * @param serverPublicKey Server public key. * @param activationId Activation ID. * @return Fingerprint of the public key. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. * @throws GenericCryptoException In case fingerprint could not be calculated. */ public String computeActivationFingerprint(PublicKey devicePublicKey, PublicKey serverPublicKey, String activationId) throws GenericCryptoException, CryptoProviderException { return computeActivationFingerprint(devicePublicKey, serverPublicKey, activationId, ActivationVersion.VERSION_3); } /** * Compute a fingerprint for the activation. The fingerprint can be used for visual validation of exchanged public keys. * * @param devicePublicKey Device public key. * @param serverPublicKey Server public key. * @param activationId Activation ID. * @param activationVersion Activation version. * @return Fingerprint of the public keys. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. * @throws GenericCryptoException In case fingerprint could not be calculated. */ public String computeActivationFingerprint(PublicKey devicePublicKey, PublicKey serverPublicKey, String activationId, ActivationVersion activationVersion) throws GenericCryptoException, CryptoProviderException { return ECPublicKeyFingerprint.compute(((ECPublicKey)devicePublicKey), (ECPublicKey)serverPublicKey, activationId, activationVersion); } /** * Returns an activation status from the encrypted activation blob as described in PowerAuth Specification. * * @param cStatusBlob Encrypted activation status blob. * @param challenge Challenge for activation status blob encryption. If non-null, then also {@code nonce} parameter must be provided. * @param nonce Nonce for activation status blob encryption. If non-null, then also {@code challenge} parameter must be provided. * @param transportKey A key used to protect the transport. * @return Status information from the status blob. * @throws InvalidKeyException When invalid key is provided. * @throws GenericCryptoException In case decryption fails. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. */ public ActivationStatusBlobInfo getStatusFromEncryptedBlob(byte[] cStatusBlob, byte[] challenge, byte[] nonce, SecretKey transportKey) throws InvalidKeyException, GenericCryptoException, CryptoProviderException { if (cStatusBlob.length != 32) { throw new GenericCryptoException("Invalid status blob size"); } // Decrypt the status blob AESEncryptionUtils aes = new AESEncryptionUtils(); byte[] iv = new KeyDerivationUtils().deriveIvForStatusBlobEncryption(challenge, nonce, transportKey); byte[] statusBlob = aes.decrypt(cStatusBlob, iv, transportKey, "AES/CBC/NoPadding"); // Prepare objects to read status info into ActivationStatusBlobInfo statusInfo = new ActivationStatusBlobInfo(); ByteBuffer buffer = ByteBuffer.wrap(statusBlob); // check if the prefix is OK int prefix = buffer.getInt(0); statusInfo.setValid(prefix == ActivationStatusBlobInfo.ACTIVATION_STATUS_MAGIC_VALUE); // fetch the activation status byte statusInfo.setActivationStatus(buffer.get(4)); // fetch the current version status byte statusInfo.setCurrentVersion(buffer.get(5)); // fetch the upgrade version status byte statusInfo.setUpgradeVersion(buffer.get(6)); // fetch ctr byte value statusInfo.setCtrByte(buffer.get(12)); // fetch the failed attempt count statusInfo.setFailedAttempts(buffer.get(13)); // fetch the max allowed failed attempt count statusInfo.setMaxFailedAttempts(buffer.get(14)); // fetch counter's look ahead window value statusInfo.setCtrLookAhead(buffer.get(15)); // extract counter data from second half of status blob byte[] ctrData = Arrays.copyOfRange(statusBlob, 16, 32); statusInfo.setCtrDataHash(ctrData); return statusInfo; } /** * Verify whether client's value of hash based counter is equal to the value received from the server. The value * received from the server is already hashed, so the function has to calculate hash from the client's counter * and then compare both values. * * @param receivedCtrDataHash Value received from the server, containing hash, calculated from hash based counter. * @param expectedCtrData Expected hash based counter. * @param transportKey Transport key. * @return {@code true} in case that received hash equals to hash calculated from counter data. * @throws InvalidKeyException When invalid key is provided. * @throws GenericCryptoException In case key derivation fails. * @throws CryptoProviderException In case cryptography provider is incorrectly initialized. */ public boolean verifyHashForHashBasedCounter(byte[] receivedCtrDataHash, byte[] expectedCtrData, SecretKey transportKey) throws CryptoProviderException, InvalidKeyException, GenericCryptoException { return new HashBasedCounterUtils().verifyHashForHashBasedCounter(receivedCtrDataHash, expectedCtrData, transportKey); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy