golang开发及数字证书研究分享

目录
  • 私钥的生成
    • RSA
    • ECDSA
    • DSA
    • 给私钥上锁(加访问密码)
  • 生成证书请求
    • go对dsa类型的证书
  • 生成证书
    • 设置CA
    • 签名算法的选择
  • 代码时间

在go语言提供的系统包中包含了大量和数字证书有关的方法。在这些方法中就有私钥生成的方法、私钥解析的方法、证书请求生成的方法、证书生成的方法等等。通过这些方法应该能够实现和openssl命令类似的功能。

仿照openssl生成证书的流程(从私钥的生成—>证书请求的生成—>证书的生成)用go语言进行模拟。

私钥的生成

在go的x509包下有go定义的证书的结构,该结构如下:

        Raw                     []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
        RawTBSCertificate       []byte // Certificate part of raw ASN.1 DER content.
        RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo.
        RawSubject              []byte // DER encoded Subject
        RawIssuer               []byte // DER encoded Issuer
        Signature          []byte
        SignatureAlgorithm SignatureAlgorithm
        PublicKeyAlgorithm PublicKeyAlgorithm
        PublicKey          interface{}
        Version             int
        SerialNumber        *big.Int
        Issuer              pkix.Name
        Subject             pkix.Name
        NotBefore, NotAfter time.Time // Validity bounds.
        KeyUsage            KeyUsage
        Extensions []pkix.Extension
        ExtraExtensions []pkix.Extension
        UnhandledCriticalExtensions []asn1.ObjectIdentifier
        ExtKeyUsage        []ExtKeyUsage           // Sequence of extended key usages.
        UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
        BasicConstraintsValid bool // if true then the next two fields are valid.
        IsCA                  bool
        MaxPathLen            int
        MaxPathLenZero bool

        SubjectKeyId   []byte
        AuthorityKeyId []byte
        OCSPServer            []string
        IssuingCertificateURL []string

        // Subject Alternate Name values
        DNSNames       []string
        EmailAddresses []string
        IPAddresses    []net.IP
        PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical.
        PermittedDNSDomains         []string
        CRLDistributionPoints []string
        PolicyIdentifiers []asn1.ObjectIdentifier

在该结构中有PublicKeyAlgorithm字段,该字段用来表示生成公钥的算法。该字段的变量中可使用的字段如下:

const (
        UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
        RSA
        DSA
        ECDSA
)

一共定义了4中情况。除去Unknown的情况。剩下的三种的实现分别在crypto/rsacrypto/dsacrypto/ecdsa这三个包中定义了实现。

RSA

使用RSA方法生成公私钥的方式非常简单。在crypto/rsa包中直接提供了生成方法。

func GenerateKey(random io.Reader, bits int) (*PrivateKey, error)

该方法生成一个rsa的私钥。查找整个包所提供的方法并没有什么方法能够生成公钥。但在包中有公钥的结构说明。查看私钥的结构:

type PrivateKey struct {
        PublicKey            // public part.
        D         *big.Int   // private exponent
        Primes    []*big.Int // prime factors of N, has >= 2 elements.
        Precomputed PrecomputedValues
}

赫然发现,公钥包含在私钥的结构中。换句话说,只要生成的私钥,公钥就同时拥有了(ECDSA和DSA的公钥也是如此)。

ECDSA

使用ECDSA生成公私钥的方式和RSA的方式非常类似:

func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error)

crypto/elliptic为参数c提供了4中实现方式。分别为:

 func P224() Curve
 func P256() Curve
 func P384() Curve
 func P521() Curve

DSA

使用DSA生成公私钥的方式和上面两种有些不同:

func GenerateKey(priv *PrivateKey, rand io.Reader) error

私钥并不是作为结果返回,而是作为参数传入。那很简单,我直接初始化一个DSA的私钥,然后把该私钥作为参数传入不就可以了嘛。事实是,仅仅是实例化了一个DSA的私钥是无法完成公私钥的生成的。生成的结果如下:

priv:&{PublicKey:{Parameters:{P:<nil> Q:<nil> G:<nil>} Y:<nil>} X:<nil>}

可以发现公钥中的所有内容都是为nil(空),由此可以说明无法只通过GenerateKey()方法生成DSA的私钥。

crypto/dsa包中还提供了:

func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) error

通过该方法的描述,可以了解到该方法是为DSA设置参数。那又如何和公私钥有关呢?,在DSA的私钥结构中包含公钥,在公钥的结构中就包含该方法所需要传入的参数Parameters。由此,我便想到可以先使用该方法对一些参数进行初始化,然后再生成私钥。

priv := &dsa.PrivateKey{}
dsa.GenerateParameters(&priv.Parameters, rand.Reader, dsa.L1024N160)
dsa.GenerateKey(priv, rand.Reader)

生成的私钥内容如下:

priv:&{PublicKey:{Parameters:{P:+91268520972047344779510472614939006285152176630742165979533208518526258287540244526987668731096217967904150874969731516661412604963023247030101570715552650277776208098462838867711078025572452557692674802977527475661989210578136725258241385474445330497234586673407237238372329018550727884900161895964574509801 Q:+767580094855879488293276223470508701563202760721 G:+42393651221310072390273970570719382707264443685255379637082820177806079494092036767507554061381644533127114802103872901363724639317297276457243780033980909021336576570837756106975221868617534717069925676009421223798208864916837561389117514471387385853288499961716794226875046226553216578582138687489881455573} Y:+68767508229940365112562020548287141674708444377336699267991474890690034611201698420418573204906537903040876819582645033160073997940957577512216430788561800033703926395782022182868300960590402743043934344374390498368316144177816214923367214895567903510165216432049170686626889267028482641530556275670781873053} X:+628682865942164859869306394087148223993136336500}

注意:Golang 对DSA证书没有完整的支持。

给私钥上锁(加访问密码)

在使用openssl进行私钥生成的时候,openssl需要我提供私钥的访问密码。那使用go进行私钥时,应该也有该功能。那应该在什么时候添加这个密码呢?是在生成私钥的时候,还是在生成pem文件的时候。我首先想到的是在生成秘密的时候,但是在crypto/rsacrypto/dsacrypto/ecdsa这三个包中查找时并没有发现任何和密码有关的词眼。那就应该在生成pem文件的时候加上密码。生成pem文件的方法在encoding/pem这个包中。但该包中只有两个编码,一个解码的方法,和密码有没有任何关系,唯一的存在的关系就是Block结构中的Header字段。

使用openssl生成的私钥文件中会存在这样的字段:

Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,02a0ba59e8cfd431

使用该字段来说明使用加密方式和提供用于解密的初始值向量。

在生成私钥和生成文件都无法把密码添加进去。那我就在想是否是在得到私钥的时候对私钥的byte数组进行加密。但这样就需要自己实现了。讲道理的话,go应该会为这种普遍性的东西提供已经封装好的方法。来回重新看api文档。发现自己漏看一个非常重要的包crypto/x509。在该包提供的方法中。很轻松的就找到了如下两个方法:

func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error)
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error)

在这两个方法中又要pem,password,恩应该就是这两个方法了,正好一个生成一个解析。

同在x509包下提供了:

func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte
func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error)

把RSA和ECDSA私钥转换成byte数组的方法,但是没有找到把DSA私钥转换成byte数组的方法。

生成证书请求

证书请求生成很简单在crypto/x509中直接提供了现成的方法。

func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv interface{}) (csr []byte, err error)

但使用用该方法有一个限制条件:

All keys types that are implemented via crypto.Signer are supported (This includes *rsa.PublicKey and *ecdsa.PublicKey.)

无法使用*dsa.PublicKey类型的公钥。而传入的参数是一个私钥,因此无法使用dsa类型的私钥。

go对dsa类型的证书

该方法需要通过一个证书请求的模板,在go中CertificateRequest是如下定义的:

Raw                      []byte // Complete ASN.1 DER content (CSR, signature algorithm and signature).
RawTBSCertificateRequest []byte // Certificate request info part of raw ASN.1 DER content.
RawSubjectPublicKeyInfo  []byte // DER encoded SubjectPublicKeyInfo.
RawSubject               []byte // DER encoded Subject.
Version            int
Signature          []byte
SignatureAlgorithm SignatureAlgorithm
PublicKeyAlgorithm PublicKeyAlgorithm
PublicKey          interface{}
Subject pkix.Name
Attributes []pkix.AttributeTypeAndValueSET
Extensions []pkix.Extension
ExtraExtensions []pkix.Extension
DNSNames       []string
EmailAddresses []string
IPAddresses    []net.IP

有一些内容可以不用填写。如果填写了,在后面生成证书时将作为内容直接填入,我就根据openssl生成证书请求时在控制台所展现的内容进行填写。即添加Subject中的内容。Subject是这样定义的:

type Name struct {
        Country, Organization, OrganizationalUnit []string
        Locality, Province                        []string
        StreetAddress, PostalCode                 []string
        SerialNumber, CommonName                  string

        Names      []AttributeTypeAndValue
        ExtraNames []AttributeTypeAndValue
}

生成证书

在go提供的crypto/x509包下并没有生成CA的方法,生成证书的方法也只有一个方法:

func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv interface{}) (cert []byte, err error)

它的参数中使用的是两个证书,和我们之前生成的CertificateRequest没有关系,而且在整个crypto/x509中的方法中都没有找到把CertificateRequest转换成Certificate的方法,而且CertificateRequest和Certificate中的部分数据结构是一样的,因此猜想是通过把CertificateRequest中的部分内容复制到Certificate中。然后再通过CreateCertificate进行签发。

如果传入的两个证书参数是一样的,那么生成的证书是一张自签发的根证书。如果传入的两张证书不同,生成的就是普通的证书了。使用的公钥和私钥是签发者的公私钥即参数parent的公私钥。和生成CertificateRequest一样,在这个方法中使用的公私钥不能是DSA类型的。

设置CA

在Certificate这个结构体中有IsCA这个字段。用来标识该证书是CA证书,但是在设置该字段为true后生成的证书在扩展中并没有显示这个证书是CA证书的。原因是在如果要使IsCA生效,需要设置BasicConstraintsValid也为true。同样的也适用于MaxPathLen这个字段。

签名算法的选择

在go中为证书的签名算法提供了常见的类型:

UnknownSignatureAlgorithm SignatureAlgorithm = iota
MD2WithRSA
MD5WithRSA
SHA1WithRSA
SHA256WithRSA
SHA384WithRSA
SHA512WithRSA
DSAWithSHA1
DSAWithSHA256
ECDSAWithSHA1
ECDSAWithSHA256
ECDSAWithSHA384
ECDSAWithSHA512

在生成证书的时候我直接选择的SHA1WITHRSA,应为我的私钥是通过RSA算法生成的,没有任何问题,但是在看go的源码中有一段生成自签名证书的测试方法。在该方法中使用了其他的签名算法。因此我想,这些签名算法的应该如何选择。当我把签名算法改成ECDSAWITHSHA1的时候,在进行签名的时候,出现了签名错误。

因此我猜猜签名算法的选择需要和签署者的公私钥的生成方式有关。

代码时间

一切用代码说话。

和生成私钥有关:

func GenRSAPriv(fileName, passwd string, len int) error {
    priv, err := rsa.GenerateKey(rand.Reader, len)
    if err != nil {
        return err
    }

    data := x509.MarshalPKCS1PrivateKey(priv)
    err = encodePrivPemFile(fileName, passwd, data)
    return err
}
//GenECDSAPriv 生成ECDSA私钥文件
func GenECDSAPriv(fileName, passwd string) error {
    priv, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
    if err != nil {
        return err
    }
    data, err := x509.MarshalECPrivateKey(priv)
    if err != nil {
        return err
    }
    err = encodePrivPemFile(fileName, passwd, data)
    return err
}
//GenDSAPriv 生成DSA私钥(用于演示)
func GenDSAPriv() {
    priv := &dsa.PrivateKey{}
    dsa.GenerateParameters(&priv.Parameters, rand.Reader, dsa.L1024N160)
    dsa.GenerateKey(priv, rand.Reader)
    fmt.Printf("priv:%+v\n", priv)
}
//DecodePriv 解析私钥文件生成私钥,(RSA,和ECDSA两种私钥格式)
func DecodePriv(fileName, passwd string) (pubkey, priv interface{}, err error) {
    data, err := ioutil.ReadFile(fileName)
    if err != nil {
        return nil, nil, errors.New("读取私钥文件错误")
    }
    block, _ := pem.Decode(data)
    data, err = x509.DecryptPEMBlock(block, []byte(passwd))
    if err != nil {
        return nil, nil, err
    }

    privKey, err := x509.ParsePKCS1PrivateKey(data) //解析成RSA私钥
    if err != nil {
        priv, err = x509.ParseECPrivateKey(data) //解析成ECDSA私钥
        if err != nil {
            return nil, nil, errors.New("支持持RSA和ECDSA格式的私钥")
        }
    }
    priv = privKey
    pubkey = &privKey.PublicKey
    return
}
//生成私钥的pem文件
func encodePrivPemFile(fileName, passwd string, data []byte) error {
    block, err := x509.EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", data, []byte(passwd), x509.PEMCipher3DES)
    if err != nil {
        return err
    }
    file, err := os.Create(fileName)
    if err != nil {
        return err
    }
    err = pem.Encode(file, block)
    if err != nil {
        return err
    }
    return nil
}

在这个代码用有一些问题:使用ECDSA生成私钥后加密的Type不知道填什么,暂时使用了”RSA PRIVATE KEY”。

和CertificateRequest有关的代码:

// EncodeCsr 生成证书请求
func EncodeCsr(country, organization, organizationlUnit, locality, province, streetAddress, postallCode []string, commonName, fileName string, priv interface{}) error {
    req := &x509.CertificateRequest{
        Subject: pkix.Name{
            Country:            country,
            Organization:       organization,
            OrganizationalUnit: organizationlUnit,
            Locality:           locality,
            Province:           province,
            StreetAddress:      streetAddress,
            PostalCode:         postallCode,
            CommonName:         commonName,
        },
    }
    data, err := x509.CreateCertificateRequest(rand.Reader, req, priv)
    if err != nil {
        return err
    }
    err = util.EncodePemFile(fileName, "CERTIFICATE REQUEST", data)
    return err
}
//DecodeCsr 解析CSRpem文件
func DecodeCsr(fileName string) (*x509.CertificateRequest, error) {
    data, err := util.DecodePemFile(fileName)
    if err != nil {
        return nil, err
    }
    req, err := x509.ParseCertificateRequest(data)
    return req, err
}

和生成Certificate有关的代码:

//GenSignselfCertificate 生成自签名证书
func GenSignselfCertificate(req *x509.CertificateRequest, publickey, privKey interface{}, fileName string, maxPath int, days time.Duration) error {
    template := &x509.Certificate{
        SerialNumber:          big.NewInt(random.Int63n(time.Now().Unix())),
        Subject:               req.Subject,
        NotBefore:             time.Now(),
        NotAfter:              time.Now().Add(days * 24 * time.Hour),
        BasicConstraintsValid: true,
        IsCA:               true,
        SignatureAlgorithm: x509.SHA1WithRSA, // 签名算法选择SHA1WithRSA
        KeyUsage:           x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDataEncipherment,
        SubjectKeyId:       []byte{1, 2, 3},
    }
    if maxPath > 0 { //如果长度超过0则设置了 最大的路径长度
        template.MaxPathLen = maxPath
    }
    cert, err := x509.CreateCertificate(rand.Reader, template, template, publickey, privKey)
    if err != nil {
        return errors.New("签发自签名证书失败")
    }
    err = util.EncodePemFile(fileName, "CERTIFICATE", cert)
    if err != nil {
        return err
    }
    return nil
}
//GenCertificate 生成非自签名证书
func GenCertificate(req *x509.CertificateRequest, parentCert *x509.Certificate, pubKey, parentPrivKey interface{}, fileName string, isCA bool, days time.Duration) error {
    template := &x509.Certificate{
        SerialNumber: big.NewInt(random.Int63n(time.Now().Unix())),
        Subject:      req.Subject,
        NotBefore:    time.Now(),
        NotAfter:     time.Now().Add(days * 24 * time.Hour),
        // ExtKeyUsage: []x509.ExtKeyUsage{ //额外的使用
        //  x509.ExtKeyUsageClientAuth,
        //  x509.ExtKeyUsageServerAuth,
        // },
        //

        SignatureAlgorithm: x509.SHA1WithRSA,
    }

    if isCA {
        template.BasicConstraintsValid = true
        template.IsCA = true
    }
    cert, err := x509.CreateCertificate(rand.Reader, template, parentCert, pubKey, parentPrivKey)
    if err != nil {
        return errors.New("签署证书失败")
    }
    err = util.EncodePemFile(fileName, "CERTIFICATE", cert)
    if err != nil {
        return err
    }
    return nil
}

在生成证书这方法,由于可设置的内容过多,不应该使用参数来对证书内容进行控制。应该和openssl一样使用配置文件的方式来对证书中的内容进行配置。

以上就是golang开发及数字证书研究分享的详细内容,更多关于golang的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go语言中通过Lua脚本操作Redis的方法

    前言 为了在我的一个基本库中降低与Redis的通讯成本,我将一系列操作封装到LUA脚本中,借助Redis提供的EVAL命令来简化操作. EVAL能够提供的特性: 可以在LUA脚本中封装若干操作,如果有多条Redis指令,封装好之后只需向Redis一次性发送所有参数即可获得结果 Redis可以保证Lua脚本运行期间不会有其他命令插入执行,提供像数据库事务一样的原子性 Redis会根据脚本的SHA值缓存脚本,已经缓存过的脚本不需要再次传输Lua代码,减少了通信成本,此外在自己代码中改变Lua脚本,执

  • Go语言开发redis封装及简单使用详解

    目录 go redis 集合操作--sadd 安装redigo 带密码的redis操作 批量添加 无密码redis操作 redis封装包 参考 go redis 集合操作--sadd redis的go语言包,我们使用官方推荐的redigo,https://github.com/garyburd/redigo 安装redigo $ go get github.com/garyburd/redigo 带密码的redis操作 package main import ( "log" "

  • Golang开发Go依赖管理工具dep安装验证实现过程

    目录 Go依赖管理工具 环境要求 目前版本 安装 验证 初始化 默认初始化 优先从$GOPATH初始化 Gopkg.toml Gopkg.lock 常用命令 dep ensure dep ensure -add dep ensure -update Go依赖管理工具 Go dependency management tool 环境要求 Golang >= 1.9Dep 目前版本 dep: version : devel build date : git hash : go version : g

  • 在Golang中使用Redis的方法示例

    周五上班的主要任务是在公司老平台上用redis处理一个队列问题,顺便复习了一下redis操作的基础知识,回来后就想着在自己的博客demo里,用redis来优化一些使用场景,学习一下golang开发下redis的使用. Redis简单介绍 简介 关于Redis的讨论,其实在现在的后台开发中已经是个老生常谈的问题,基本上也是后端开发面试的基本考察点.其中 Redis的背景介绍和细节说明在这里就不赘述.不管怎么介绍,核心在于Redis是一个基于内存的key-value的多数据结构存储,并可以提供持久化

  • golang开发安装go-torch火焰图操作步骤

    目录 安装 使用 另一种自定义显示方式 代码修改 使用 查看 安装 1. 安装go-torch go get github.com/uber/go-torch 2.安装FlameGraph cd $GOPATH && git clone  https://github.com/brendangregg/FlameGraph.git export PATH=$PATH:$GOPATH/FlameGraph [这步一定要设置,生成火焰图时会用到] 3.安装graphviz (CentOS, R

  • golang开发go包依赖管理godep使用教程

    目录 介绍 安装 包管理使用 godep 拉取依赖 restore 用于开发 检出依赖 save godep 编译运行 build Godeps目录的作用 godep其他命令 介绍 godep是解决包依赖的管理工具,目前最主流的一种,原理是扫描记录版本控制的信息,并在go命令前加壳来做到依赖管理 godep 建议在 golang 1.6 以后使用 godep 依赖 vendor 具体查看 https://stackoverflow.com/questions/37237036/how-shoul

  • golang开发及数字证书研究分享

    目录 私钥的生成 RSA ECDSA DSA 给私钥上锁(加访问密码) 生成证书请求 go对dsa类型的证书 生成证书 坑 设置CA 签名算法的选择 代码时间 在go语言提供的系统包中包含了大量和数字证书有关的方法.在这些方法中就有私钥生成的方法.私钥解析的方法.证书请求生成的方法.证书生成的方法等等.通过这些方法应该能够实现和openssl命令类似的功能. 仿照openssl生成证书的流程(从私钥的生成->证书请求的生成->证书的生成)用go语言进行模拟. 私钥的生成 在go的x509包下有

  • 使用dep 配置golang 开发环境的操作方法

    概要 golang 的包管理一直没有官方统一的解决方案,因此也产生了很多非官方的包管理工具. 之前我一直使用的 gb(https://getgb.io/) 能够很好的隔开各个 golang 工程,当时 gb 创建的工程不太融入已有的 GOPATH 中. gb 相当于是把工程的目录作为 GOPATH,并且它的 vendor 目录也和 golang 自己的 vendor 不太一样. dep 的 Roadmap 中已经制定了成为 golang 官方包管理工具的计划, 所以,使用 dep 来组织自己的

  • Android 安全加密:数字签名和数字证书详解

    Android安全加密专题文章索引 Android安全加密:对称加密 Android安全加密:非对称加密 Android安全加密:消息摘要Message Digest Android安全加密:数字签名和数字证书 Android安全加密:Https编程 以上学习所有内容,对称加密.非对称加密.消息摘要.数字签名等知识都是为了理解数字证书工作原理而作为一个预备知识.数字证书是密码学里的终极武器,是人类几千年历史总结的智慧的结晶,只有在明白了数字证书工作原理后,才能理解Https 协议的安全通讯机制.

  • 纯Java实现数字证书生成签名的简单实例

    package com.ylsoft.cert; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator;

  • Python生成数字图片代码分享

    本文向大家分享了几段Python生成数字图片的代码,喜欢的朋友可以参考.具体如下: 最终版本 # -*- coding:utf-8 -*- from PIL import Image,ImageFont,ImageDraw,ImageFilter import random import os import time class Code(object): def __init__(self, imgSize=(35,35),\ fontSize=25, bgColor=(255,)*4, fo

  • 使用mint-ui开发项目的一些心得(分享)

    Mint-ui: 1.Cell 开发过程中,有时需要修改mt-cell自带的icon和value的标签样式,这时我们可以在<mt-cell></mt-cell>内新添如下标签进行改写: <si-cell class="user-cell item-cell" title="当前产品" to="/selectproduct" is-link> <i class="siiconfont siicon

  • iOS开发之数字每隔3位用逗号分隔

    demo: 源码下载:http://xiazai.jb51.net/201709/yuanma/DMLimtDemo_jb51.rar 之前做电商类APP时,针对稍大的金额展示时,需要每隔千位添加逗号便于用户识别. 逻辑比较简单,这里按需要写了一个可选前后缀的一个转换方法: #import "DMViewController.h" @interface DMViewController () @property (weak, nonatomic) IBOutlet UITextFiel

  • python游戏开发的五个案例分享

    一.序列应用--猜单词游戏 1. 游戏介绍 猜单词游戏就是计筧机随机产生一个单词,打乱字母顺序,供玩家去猜测.此游戏采用控制字符界面, 2.  程序设计思路 游戏中,可使用序列中的元组存储所有待猜测的单词.因为猜单词游戏需要随机产生某个待猜测单词以及随机数字,所以引入random模块随机数函数.其中,random.choice()可以从序列中随机选取元素. 在游戏中,随机挑出一个单词word后,把单词word的字母顺序打乱的方法是随机单词字符串中选择一个位置position,把position位

  • Python 使用 PyQt5 开发的关机小工具分享

    前两天简单认识了一下PyQt5,通过练习开发了一款在Window下自定义关机的小工具, 代码如下: import os,sys,time from PyQt5 import QtCore,QtWidgets,QtGui class guanji(object): def pageShow(self,page): #设置窗口的位置和大小 page.setGeometry(400,400,400,200) #设置窗口的标题 page.setWindowTitle('Window shutdown')

  • 详解golang开发中select多路选择

    select 是 Golang 中的一个控制结构,语法上类似于switch 语句,只不过select是用于 goroutine 间通信的 ,每个 case 必须是一个通信操作,要么是发送要么是接收,select 会随机执行一个可运行的 case.如果没有 case 可运行,goroutine 将阻塞,直到有 case 可运行. select 多路选择 select写法上跟switch case的写法基本一致,只不过golang的select是通信控制语句.select的执行必须有通信的发送或者接

随机推荐