Eclipse Milo

时间:2016-09-13 09:43:07

标签: java eclipse authentication opc-ua milo

Eclipse Milo中是否有可能客户端使用此身份验证参数连接到服务器:“certificate + private key”? 还有参数“安全策略”和“消息安全模式”?

(如在UAExpert客户端中:http://documentation.unified-automation.com/uaexpert/1.4.0/html/connect.html

如果是,那怎么样?

我随时可以使用:

  • 一个“.perm”文件格式的私钥;
  • 一个“.der”文件格式的证书;
  • 服务器的一个CA为“.der”文件格式;
  • 以及“.der”文件格式的4096位服务器的CA;

2 个答案:

答案 0 :(得分:1)

是的,它目前是可能的,虽然现在不像使用用户名/密码那样“轻松”。

客户端SDK公开了一个名为IdentityProvider的接口,该接口在客户端连接时被委派给它,并被赋予端点和服务器随机数。它返回一个包含UserIdentityTokenSignatureData的2元组。

您需要为X509IdentityToken案例实施此界面并返回您的证书(在X509IdentityToken中),并证明您拥有密钥(在SignatureData中) )。

获得此IdentityProvider后,您只需在构建setIdentityProvider对象时调用OpcUaClientConfig,即可告知客户端使用它。

由于这有点麻烦,而且SDK的目的是减轻用户的负担,我也会把它作为Milo的功能票。如果你不能自己实现它,我可以在本周开始实现它。

答案 1 :(得分:0)

我已经使用他的班级 X509IdentityProvider Kevin Herron的帮助下解决了我的问题。

以下是解决方案代码:

  

PemFile.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

public class PemFile {

    private PemObject pemObject;

    public PemFile(String filename) throws FileNotFoundException, IOException {
        PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename)));
        try {
            this.pemObject = pemReader.readPemObject();
        } finally {
            pemReader.close();
        }
    }

    public PemObject getPemObject() {
        return pemObject;
    }
}
  

X509IdentityProvider.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
import org.eclipse.milo.opcua.stack.core.types.enumerated.UserTokenType;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.SignatureData;
import org.eclipse.milo.opcua.stack.core.types.structured.UserIdentityToken;
import org.eclipse.milo.opcua.stack.core.types.structured.UserTokenPolicy;
import org.eclipse.milo.opcua.stack.core.types.structured.X509IdentityToken;
import org.eclipse.milo.opcua.stack.core.util.SignatureUtil;
import org.jooq.lambda.tuple.Tuple2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class X509IdentityProvider implements IdentityProvider {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final X509Certificate certificate;
    private final PrivateKey privateKey;

    public X509Certificate getCertificate() {
        return certificate;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public X509IdentityProvider(X509Certificate certificate, PrivateKey privateKey) {
        this.certificate = certificate;
        this.privateKey = privateKey;
    }

    public X509IdentityProvider(String certificate, String privateKey) {
        this.certificate = loadCertificateFromDerFile(certificate);

        Security.addProvider(new BouncyCastleProvider());
        KeyFactory kf;
        PrivateKey privateKeyTmp = null;
        try {
            kf = KeyFactory.getInstance("RSA", "BC");
            privateKeyTmp = loadPrivateKeyFromPemFile(kf, privateKey);
        } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | IOException e) {
            e.printStackTrace();
        }
        this.privateKey = privateKeyTmp;

    }

    @Override
    public Tuple2<UserIdentityToken, SignatureData> getIdentityToken(EndpointDescription endpoint,
            ByteString serverNonce) throws Exception {
        UserTokenPolicy tokenPolicy = Arrays.stream(endpoint.getUserIdentityTokens())
                .filter(t -> t.getTokenType() == UserTokenType.Certificate).findFirst()
                .orElseThrow(() -> new Exception("no x509 certificate token policy found"));
        String policyId = tokenPolicy.getPolicyId();
        SecurityPolicy securityPolicy = SecurityPolicy.Basic256;
        String securityPolicyUri = tokenPolicy.getSecurityPolicyUri();
        try {
            if (securityPolicyUri != null && !securityPolicyUri.isEmpty()) {
                securityPolicy = SecurityPolicy.fromUri(securityPolicyUri);
            } else {
                securityPolicyUri = endpoint.getSecurityPolicyUri();
                securityPolicy = SecurityPolicy.fromUri(securityPolicyUri);
            }
        } catch (Throwable t) {
            logger.warn("Error parsing SecurityPolicy for uri={}", securityPolicyUri);
        }
        X509IdentityToken token = new X509IdentityToken(policyId, ByteString.of(certificate.getEncoded()));
        SignatureData signatureData;
        ByteString serverCertificate = endpoint.getServerCertificate();
        byte[] serverCertificateBytes = serverCertificate.isNotNull() ? serverCertificate.bytes() : new byte[0];
        byte[] serverNonceBytes = serverNonce.isNotNull() ? serverNonce.bytes() : new byte[0];
        assert serverCertificateBytes != null;
        assert serverNonceBytes != null;
        byte[] signature = SignatureUtil.sign(securityPolicy.getAsymmetricSignatureAlgorithm(), privateKey,
                ByteBuffer.wrap(serverCertificateBytes), ByteBuffer.wrap(serverNonceBytes));
        signatureData = new SignatureData(securityPolicy.getAsymmetricSignatureAlgorithm().getUri(),
                ByteString.of(signature));
        return new Tuple2<>(token, signatureData);
    }


    private static X509Certificate loadCertificateFromDerFile(String filename) {
        InputStream in;
        X509Certificate cert = null;
        try {
            in = new FileInputStream(filename);

            CertificateFactory factory = CertificateFactory.getInstance("X.509");
             cert = (X509Certificate) factory.generateCertificate(in);
        } catch (FileNotFoundException | CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return cert;
    }

    private static PrivateKey loadPrivateKeyFromPemFile(KeyFactory factory, String filename)
            throws InvalidKeySpecException, FileNotFoundException, IOException {
        PemFile pemFile = new PemFile(filename);
        byte[] content = pemFile.getPemObject().getContent();
        PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
        return factory.generatePrivate(privKeySpec);
    }

}
  

ClientRunner.java

...
...

private OpcUaClient createClient() throws Exception {
        SecurityPolicy securityPolicy = clientExample.getSecurityPolicy(); // For example : SecurityPolicy.Basic256
        String securityMode = clientExample.getSecurityMode(); // For example : "SignAndEncrypt"

        EndpointDescription[] endpoints = UaTcpStackClient.getEndpoints(endpointUrl).get();

        EndpointDescription endpoint = Arrays.stream(endpoints)
                .filter(e -> e.getSecurityPolicyUri().equals(securityPolicy.getSecurityPolicyUri()))//
                .filter(e -> e.getSecurityMode().toString().compareTo(securityMode) == 0)//
                .findFirst()//
                .orElseThrow(() -> new Exception("no desired endpoints returned"));

        logger.info("Using endpoint: {} [{}]", endpoint.getEndpointUrl(), securityPolicy);

        loader.load();

        // Mode : securityPolicy == SecurityPolicy.Basic256 && securityMode.compareTo("SignAndEncrypt") == 0)
        X509IdentityProvider x509IdentityProvider = new X509IdentityProvider("/certificate.der",
                "/privateKey.pem");
        X509Certificate cert = x509IdentityProvider.getCertificate();
        KeyPair keyPair = new KeyPair(cert.getPublicKey(), x509IdentityProvider.getPrivateKey());
        OpcUaClientConfig config = OpcUaClientConfig.builder().setApplicationName(LocalizedText.english("opc-ua client"))//
                .setApplicationUri("urn:opcua client")//
                .setCertificate(cert)//
                .setKeyPair(keyPair)//
                .setEndpoint(endpoint)//
                .setIdentityProvider(x509IdentityProvider)//
                .setIdentityProvider(clientExample.getIdentityProvider())//
                .setRequestTimeout(uint(5000))//
                .build();


        return new OpcUaClient(config);
    }